We always HAVE_CONFIG_H so don't bother checking whether we have it or not.
[metze/wireshark/wip.git] / ui / gtk / prefs_filter_expressions.c
1 /* prefs_filter_expressions.c
2  * Submitted by Edwin Groothuis <wireshark@mavetju.org>
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <string.h>
28
29 #include <gtk/gtk.h>
30
31 #include <epan/prefs.h>
32 #include <epan/column_info.h>
33 #include <epan/column.h>
34 #include <epan/strutil.h>
35 #include <epan/filter_expressions.h>
36
37 #include "../globals.h"
38
39 #include "ui/gtk/gtkglobals.h"
40 #include "ui/gtk/gui_utils.h"
41 #include "ui/gtk/packet_list.h"
42 #include "ui/gtk/filter_dlg.h"
43 #include "ui/gtk/filter_autocomplete.h"
44 #include "ui/gtk/filter_expression_save_dlg.h"
45 #include "ui/gtk/old-gtk-compat.h"
46
47 static void filter_expressions_list_new_cb(GtkWidget *, gpointer);
48 static void filter_expressions_list_remove_cb(GtkWidget *, gpointer);
49 static gboolean filter_expressions_label_changed_cb(GtkCellRendererText *, const gchar *, const gchar *, gpointer);
50 static gboolean filter_expressions_expression_changed_cb(GtkCellRendererText *, const gchar *, const gchar *, gpointer);
51
52 #define E_FILTER_EXPRESSION_COLUMNL     "filter_expression_columnl"
53 #define E_FILTER_EXPRESSION_STORE       "filter_expression_store"
54
55 enum {
56     ENABLED_COLUMN,
57     LABEL_COLUMN,
58     EXPRESSION_COLUMN,
59     DATA_COLUMN,
60     N_COLUMN /* The number of columns */
61 };
62
63 /* Visible toggled */
64 static void
65 enable_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
66 {
67     GtkTreeModel    *model = (GtkTreeModel *)data;
68     GtkTreeIter      iter;
69     GtkTreePath     *path = gtk_tree_path_new_from_string(path_str);
70     struct filter_expression *fe;
71
72     gtk_tree_model_get_iter(model, &iter, path);
73     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
74
75     fe->enabled = !fe->enabled;
76
77     gtk_list_store_set(GTK_LIST_STORE(model), &iter, ENABLED_COLUMN,
78         fe->enabled, -1);
79
80     gtk_tree_path_free(path);
81 } /* visible_toggled */
82
83 /*
84  * Create and display the column selection widgets.
85  * Called as part of the creation of the Preferences notebook ( Edit ! Preferences )
86  */
87 GtkWidget *
88 filter_expressions_prefs_show(void) {
89     GtkWidget         *main_vb, *bottom_hb, *column_l, *add_bt, *remove_bt;
90     GtkWidget         *list_vb, *list_lb, *list_sc;
91     GtkWidget         *add_remove_hb;
92     GtkListStore      *store;
93     GtkCellRenderer   *renderer;
94     GtkTreeViewColumn *column;
95     GtkTreeSelection  *sel;
96     GtkTreeIter        iter;
97     GtkTreeIter        first_iter;
98     gint               first_row = TRUE;
99     struct filter_expression *fe;
100     const gchar       *column_titles[] = {"Enabled", "Label",
101                                           "Filter Expression"};
102
103     /* Container for each row of widgets */
104     main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
105     gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
106     gtk_widget_show(main_vb);
107
108     list_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
109     gtk_container_set_border_width(GTK_CONTAINER(list_vb), 5);
110     gtk_widget_show(list_vb);
111     gtk_box_pack_start(GTK_BOX(main_vb), list_vb, TRUE, TRUE, 0);
112
113     list_lb = gtk_label_new(("[The first list entry will be displayed as the "
114         "first button right of the Save button - Drag and drop entries to "
115         "change column order]"));
116     gtk_widget_show(list_lb);
117     gtk_box_pack_start(GTK_BOX(list_vb), list_lb, FALSE, FALSE, 0);
118
119     list_sc = scrolled_window_new(NULL, NULL);
120     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(list_sc),
121         GTK_SHADOW_IN);
122     gtk_box_pack_start(GTK_BOX(list_vb), list_sc, TRUE, TRUE, 0);
123     gtk_widget_show(list_sc);
124
125     store = gtk_list_store_new(N_COLUMN,
126                                G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
127                                G_TYPE_POINTER);
128
129     column_l = tree_view_new(GTK_TREE_MODEL(store));
130     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(column_l), TRUE);
131     gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(column_l), FALSE);
132     gtk_tree_view_set_reorderable(GTK_TREE_VIEW(column_l), TRUE);
133     gtk_widget_set_tooltip_text(column_l, "Click on a label or expression to "
134         "change its name.\nDrag an item to change its order.\nTick 'Enable' "
135         "to enable the filter in the buttons.");
136
137     /* Enabled button */
138     renderer = gtk_cell_renderer_toggle_new();
139     g_signal_connect(renderer, "toggled", G_CALLBACK(enable_toggled), store);
140     column = gtk_tree_view_column_new_with_attributes(
141         column_titles[ENABLED_COLUMN], renderer, "active", ENABLED_COLUMN,
142         NULL);
143     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
144     gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
145
146     /* Label editor */
147     renderer = gtk_cell_renderer_text_new();
148     g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
149     g_signal_connect(renderer, "edited",
150         G_CALLBACK(filter_expressions_label_changed_cb), GTK_TREE_MODEL(store));
151     column = gtk_tree_view_column_new_with_attributes(
152         column_titles[LABEL_COLUMN], renderer, "text", LABEL_COLUMN, NULL);
153     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
154     gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
155
156     /* Expression editor */
157     renderer = gtk_cell_renderer_text_new();
158     g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
159     g_signal_connect(renderer, "edited",
160         G_CALLBACK(filter_expressions_expression_changed_cb),
161         GTK_TREE_MODEL(store));
162     column = gtk_tree_view_column_new_with_attributes(
163         column_titles[EXPRESSION_COLUMN], renderer, "text", EXPRESSION_COLUMN,
164         NULL);
165     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
166     gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
167
168     /* XXX - make this match the packet list prefs? */
169     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(column_l));
170     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
171
172     gtk_container_add(GTK_CONTAINER(list_sc), column_l);
173     gtk_widget_show(column_l);
174
175     fe = *pfilter_expression_head;
176     while (fe != NULL) {
177         fe->index = -1;
178         gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
179             ENABLED_COLUMN, fe->enabled,
180             LABEL_COLUMN, fe->label,
181             EXPRESSION_COLUMN, fe->expression,
182             DATA_COLUMN, fe,
183             -1);
184
185         if (first_row) {
186             first_iter = iter;
187             first_row = FALSE;
188         }
189         fe = fe->next;
190     }
191     g_object_unref(G_OBJECT(store));
192
193     /* Bottom row: Add/remove buttons */
194     bottom_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
195     gtk_box_pack_start(GTK_BOX(main_vb), bottom_hb, FALSE, TRUE, 0);
196     gtk_widget_show(bottom_hb);
197
198     /* Add button */
199     add_remove_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, TRUE);
200     gtk_container_set_border_width(GTK_CONTAINER(add_remove_hb), 5);
201     gtk_box_pack_start(GTK_BOX(bottom_hb), add_remove_hb, FALSE, FALSE, 0);
202     gtk_widget_show(add_remove_hb);
203
204     add_bt = gtk_button_new_from_stock(GTK_STOCK_ADD);
205     g_signal_connect(add_bt, "clicked",
206         G_CALLBACK(filter_expressions_list_new_cb), column_l);
207     gtk_box_pack_start(GTK_BOX(add_remove_hb), add_bt, FALSE, FALSE, 0);
208     gtk_widget_set_tooltip_text(add_bt,
209         "Add a new row at the end of the list.");
210     gtk_widget_show(add_bt);
211
212     /* Remove button */
213     remove_bt = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
214     g_signal_connect(remove_bt, "clicked",
215         G_CALLBACK(filter_expressions_list_remove_cb), column_l);
216     gtk_box_pack_start(GTK_BOX(add_remove_hb), remove_bt, FALSE, FALSE, 0);
217     gtk_widget_set_tooltip_text(remove_bt, "Remove the selected row.");
218     gtk_widget_show(remove_bt);
219
220     /* select the first menu list row.             */
221     /*  Triggers call to column_list_select_cb().  */
222     if (first_row == FALSE)
223         gtk_tree_selection_select_iter(sel, &first_iter);
224
225     g_object_set_data(G_OBJECT(main_vb), E_FILTER_EXPRESSION_COLUMNL,
226         column_l);
227     g_object_set_data(G_OBJECT(main_vb), E_FILTER_EXPRESSION_STORE,
228         store);
229
230     return(main_vb);
231 }
232
233 static void
234 filter_expressions_list_remove_cb(GtkWidget *w _U_, gpointer data)
235 {
236     GtkTreeView      *column_l = GTK_TREE_VIEW(data);
237     GtkTreeSelection *sel;
238     GtkTreeModel     *model;
239     GtkTreeIter       iter;
240     struct filter_expression *fe;
241
242     sel = gtk_tree_view_get_selection(column_l);
243     if (!gtk_tree_selection_get_selected(sel, &model, &iter))
244         return;
245
246     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
247     fe->deleted = TRUE;
248
249     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
250 }
251
252 static void
253 filter_expressions_list_new_cb(GtkWidget *w _U_, gpointer data _U_)
254 {
255     const gchar       *label = "New Label";
256     const gchar       *expression = "New Expression";
257     GtkTreeView       *fe_l = GTK_TREE_VIEW(data);
258     GtkTreeModel      *model;
259     GtkTreeIter        iter;
260     GtkTreePath       *path;
261     GtkTreeViewColumn *label_column;
262     struct filter_expression *fe;
263
264     fe = filter_expression_new(label, expression, TRUE);
265
266     model = gtk_tree_view_get_model(fe_l);
267     gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, G_MAXINT,
268         ENABLED_COLUMN, fe->enabled,
269         LABEL_COLUMN, fe->label,
270         EXPRESSION_COLUMN, fe->expression,
271         DATA_COLUMN, fe,
272         -1);
273
274     /* Triggers call to column_list_select_cb()   */
275     gtk_tree_selection_select_iter(gtk_tree_view_get_selection(fe_l), &iter);
276
277     /* Set the cursor to the 'Title' column of the newly added row and enable
278      * editing
279      * XXX: If displaying the new title ["New column"] widens the title column
280      * of the treeview, then the set_cursor below doesn't properly generate an
281      * entry box around the title text. The width of the box appears to be the
282      * column width before the treeview title column was widened. Seems like a
283      * bug...
284      *
285      *      I haven't found a work-around.
286      */
287     path = gtk_tree_model_get_path(model, &iter);
288     label_column = gtk_tree_view_get_column(fe_l, 2);
289     gtk_tree_view_set_cursor(fe_l, path, label_column, TRUE);
290     gtk_tree_path_free(path);
291 }
292
293
294 static gboolean
295 filter_expressions_expression_changed_cb(GtkCellRendererText *cell _U_, const gchar *str_path, const gchar *new_expression, gpointer data)
296 {
297     struct filter_expression *fe;
298     GtkTreeModel *model = (GtkTreeModel *)data;
299     GtkTreePath  *path = gtk_tree_path_new_from_string(str_path);
300     GtkTreeIter   iter;
301
302     gtk_tree_model_get_iter(model, &iter, path);
303     gtk_list_store_set(GTK_LIST_STORE(model), &iter, EXPRESSION_COLUMN,
304         new_expression, -1);
305
306     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
307     if (fe != NULL) {
308         g_free(fe->expression);
309         fe->expression = g_strdup(new_expression);
310     }
311
312     gtk_tree_path_free(path);
313     return(TRUE);
314 }
315
316 static gboolean
317 filter_expressions_label_changed_cb(GtkCellRendererText *cell _U_, const gchar *str_path, const gchar *new_label, gpointer data)
318 {
319     struct filter_expression *fe;
320     GtkTreeModel *model = (GtkTreeModel *)data;
321     GtkTreePath  *path = gtk_tree_path_new_from_string(str_path);
322     GtkTreeIter   iter;
323
324     gtk_tree_model_get_iter(model, &iter, path);
325     gtk_list_store_set(GTK_LIST_STORE(model), &iter, LABEL_COLUMN, new_label,
326         -1);
327
328     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
329     if (fe != NULL) {
330         g_free(fe->label);
331         fe->label = g_strdup(new_label);
332     }
333
334     gtk_tree_path_free(path);
335     return TRUE;
336 }
337
338
339 void
340 filter_expressions_prefs_fetch(GtkWidget *w)
341 {
342     gboolean      items_left;
343     GtkTreeModel *model;
344     GtkTreeView  *column_l;
345     GtkTreeIter   iter;
346     GtkListStore *store;
347     struct filter_expression *fe;
348     gint          first_row = TRUE;
349     gint          index = 0;
350
351     column_l = (GtkTreeView *)g_object_get_data(G_OBJECT(w),
352         E_FILTER_EXPRESSION_COLUMNL);
353     model = gtk_tree_view_get_model(column_l);
354     store = (GtkListStore *)g_object_get_data(G_OBJECT(w),
355         E_FILTER_EXPRESSION_STORE);
356
357     /* Record the order of the items in the list.  */
358     items_left = gtk_tree_model_get_iter_first(model, &iter);
359     while (items_left) {
360         gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
361         if (fe != NULL)
362             fe->index = index++;
363         items_left = gtk_tree_model_iter_next (model, &iter);
364     }
365
366     filter_expression_reinit(FILTER_EXPRESSION_REINIT_DESTROY | FILTER_EXPRESSION_REINIT_CREATE);
367
368     gtk_list_store_clear(store);
369     fe = *pfilter_expression_head;
370     while (fe != NULL) {
371         fe->index = -1;
372         gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
373             ENABLED_COLUMN, fe->enabled,
374             LABEL_COLUMN, fe->label,
375             EXPRESSION_COLUMN, fe->expression,
376             DATA_COLUMN, fe,
377             -1);
378
379         if (first_row) {
380             first_row = FALSE;
381         }
382         fe = fe->next;
383     }
384 }