Yell at the user less.
[metze/wireshark/wip.git] / ui / gtk / uat_gui.c
1 /*
2  *  uat_gui.c
3  *
4  *  User Accessible Tables GUI
5  *  Maintain an array of user accessible data strucures
6  *
7  * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
8  *
9  * Wireshark - Network traffic analyzer
10  * By Gerald Combs <gerald@wireshark.org>
11  * Copyright 2001 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 /*
29  * TO DO:
30  * + improvements
31  *   - field value check (red/green editbox)
32  *   - tooltips (add field descriptions)
33  * - Make cells editable
34  * - Allow reordering via drag and drop
35  */
36
37 #include "config.h"
38 #include <string.h>
39 #include <stdlib.h>
40 #include <math.h>
41
42 #include <gtk/gtk.h>
43 #if GTK_CHECK_VERSION(3,0,0)
44 # include <gdk/gdkkeysyms-compat.h>
45 #endif
46
47 #include <wsutil/report_err.h>
48
49 #include <epan/dfilter/dfilter-macro.h>
50 #include <epan/proto.h>
51 #include <epan/packet.h>
52 #include <epan/uat-int.h>
53 #include <epan/value_string.h>
54 #include <wsutil/filesystem.h>
55
56 #include <epan/stat_groups.h>
57
58 #include "ui/help_url.h"
59 #include "ui/gtk/gtkglobals.h"
60 #include "ui/gtk/gui_utils.h"
61 #include "ui/gtk/dlg_utils.h"
62 #include "ui/gtk/stock_icons.h"
63 #include "ui/gtk/gui_stat_menu.h"
64 #include "ui/gtk/main.h"
65 #include "ui/gtk/uat_gui.h"
66 #include "ui/gtk/packet_list.h"
67 #include "ui/gtk/webbrowser.h"
68 #include "ui/gtk/old-gtk-compat.h"
69 #include "ui/gtk/packet_win.h"
70
71 # define BUTTON_SIZE_X -1
72 # define BUTTON_SIZE_Y -1
73
74 struct _uat_rep_t {
75         GtkWidget    *window;
76         GtkWidget    *vbox;
77         GtkWidget    *scrolledwindow;
78         GtkTreeView  *list;
79         GtkListStore *list_store;
80         GtkWidget    *bbox;
81         GtkWidget    *bt_new;
82         GtkWidget    *bt_edit;
83         GtkWidget    *bt_copy;
84         GtkWidget    *bt_delete;
85         GtkWidget    *bt_refresh;
86         GtkWidget    *bt_clear;
87         GtkWidget    *bt_up;
88         GtkWidget    *bt_down;
89         GtkWidget    *bt_apply;
90         GtkWidget    *bt_cancel;
91         GtkWidget    *bt_ok;
92         GtkWidget    *unsaved_window;
93
94         gint          selected;
95 };
96
97 struct _str_pair {
98         const char *ptr;
99         guint       len;
100 };
101
102 struct _uat_dlg_data {
103         GtkWidget *win;
104         GPtrArray *entries;
105         uat_t     *uat;
106         void      *rec;
107         gboolean   is_new;
108         gint       row;
109         GPtrArray *tobe_freed;
110 };
111
112 static gboolean unsaved_dialog(GtkWindow *w, GdkEvent *e, gpointer u);
113 static gboolean uat_window_delete_event_cb(GtkWindow *w, GdkEvent *e, gpointer u);
114
115 static void set_buttons(uat_t *uat, gint row) {
116
117         if (!uat->rep) return;
118
119         if (row > 0) {
120                 gtk_widget_set_sensitive (uat->rep->bt_up, TRUE);
121         } else {
122                 gtk_widget_set_sensitive (uat->rep->bt_up, FALSE);
123         }
124
125         if (row < (gint)(uat->raw_data->len - 1) && row >= 0) {
126                 gtk_widget_set_sensitive (uat->rep->bt_down, TRUE);
127         } else {
128                 gtk_widget_set_sensitive (uat->rep->bt_down, FALSE);
129         }
130
131         gtk_widget_set_sensitive (uat->rep->bt_new, TRUE);
132         gtk_widget_set_sensitive (uat->rep->bt_clear, TRUE);
133
134         if (row < 0) {
135                 gtk_widget_set_sensitive (uat->rep->bt_edit, FALSE);
136                 gtk_widget_set_sensitive (uat->rep->bt_copy, FALSE);
137                 gtk_widget_set_sensitive (uat->rep->bt_delete, FALSE);
138         } else {
139                 gtk_widget_set_sensitive (uat->rep->bt_edit, TRUE);
140                 gtk_widget_set_sensitive (uat->rep->bt_copy, TRUE);
141                 gtk_widget_set_sensitive (uat->rep->bt_delete, TRUE);
142         }
143
144         if (uat->changed) {
145                 g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
146                 g_signal_connect(uat->rep->window, "delete_event", G_CALLBACK(unsaved_dialog), uat);
147                 g_signal_connect(uat->rep->window, "destroy", G_CALLBACK(unsaved_dialog), uat);
148         } else {
149                 g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
150                 g_signal_connect(GTK_WINDOW(uat->rep->window), "delete_event", G_CALLBACK(uat_window_delete_event_cb), uat);
151                 g_signal_connect(GTK_WINDOW(uat->rep->window), "destroy", G_CALLBACK(uat_window_delete_event_cb), uat);
152         }
153 }
154
155 static void limit_buttons(uat_t *uat) {
156
157         if (!uat->rep) return;
158
159         gtk_widget_set_sensitive (uat->rep->bt_up, FALSE);
160         gtk_widget_set_sensitive (uat->rep->bt_down, FALSE);
161
162         gtk_widget_set_sensitive (uat->rep->bt_new, FALSE);
163         gtk_widget_set_sensitive (uat->rep->bt_edit, FALSE);
164         gtk_widget_set_sensitive (uat->rep->bt_copy, FALSE);
165         gtk_widget_set_sensitive (uat->rep->bt_delete, FALSE);
166
167         gtk_widget_set_sensitive (uat->rep->bt_clear, FALSE);
168 }
169
170 static char *fld_tostr(void *rec, uat_field_t *f) {
171         guint       len;
172         char       *ptr;
173         char       *out;
174
175         f->cb.tostr(rec, &ptr, &len, f->cbdata.tostr, f->fld_data);
176
177         switch(f->mode) {
178             case PT_TXTMOD_NONE:
179                 case PT_TXTMOD_STRING:
180                 case PT_TXTMOD_ENUM:
181                 case PT_TXTMOD_FILENAME:
182                 case PT_TXTMOD_DIRECTORYNAME:
183                         out = g_strndup(ptr, len);
184                         break;
185                 case PT_TXTMOD_HEXBYTES: {
186                         GString *s = g_string_sized_new( len*2 + 1 );
187                         guint i;
188
189                         for (i=0; i<len;i++) g_string_append_printf(s, "%.2X", ((const guint8*)ptr)[i]);
190
191                         out = g_strdup(s->str);
192
193                         g_string_free(s, TRUE);
194                         break;
195                 }
196                 default:
197                         g_assert_not_reached();
198                         out = NULL;
199                         break;
200         }
201
202         g_free(ptr);
203         return out;
204 }
205
206
207
208 static void append_row(uat_t *uat, guint idx) {
209         void        *rec = UAT_INDEX_PTR(uat, idx);
210         uat_field_t *f   = uat->fields;
211         guint        colnum;
212         GtkTreeIter  iter;
213         gchar* tmp_str;
214
215         if (! uat->rep) return;
216
217         gtk_list_store_insert_before(uat->rep->list_store, &iter, NULL);
218         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
219                 tmp_str = fld_tostr(rec, &(f[colnum]));
220                 gtk_list_store_set(uat->rep->list_store, &iter, colnum, tmp_str, -1);
221                 g_free(tmp_str);
222         }
223 }
224
225 static void reset_row(uat_t *uat, guint idx) {
226         void        *rec = UAT_INDEX_PTR(uat, idx);
227         uat_field_t *f   = uat->fields;
228         guint        colnum;
229         GtkTreePath *path;
230         GtkTreeIter  iter;
231         gchar* tmp_str;
232
233         if (! uat->rep) return;
234
235         path = gtk_tree_path_new_from_indices(idx, -1);
236         if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(uat->rep->list_store), &iter, path)) {
237                 return;
238         }
239
240         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
241                 tmp_str = fld_tostr(rec, &(f[colnum]));
242                 gtk_list_store_set(uat->rep->list_store, &iter, colnum, tmp_str, -1);
243                 g_free(tmp_str);
244         }
245 }
246
247 static guint8 *unhexbytes(const char *si, guint len, guint *len_p, char** err) {
248         guint8       *buf;
249         guint8       *p;
250         const guint8 *s = (const guint8 *)si;
251         guint         i;
252
253         if (len % 2) {
254                 *err = g_strdup_printf("Uneven number of chars hex string %u \n'%s'", len, si);
255                 return NULL;
256         }
257
258         buf = (guint8 *)g_malloc(len/2+1);
259         p = buf;
260
261         for (i = 0; i<len ; i += 2) {
262                 guint8 lo = s[i+1];
263                 guint8 hi = s[i];
264
265                 if (hi >= '0' && hi <= '9') {
266                         hi -= '0';
267                 } else if (hi >= 'a' && hi <= 'f') {
268                         hi -=  'a';
269                         hi += 0xa;
270                 } else if (hi >= 'A' && hi <= 'F') {
271                         hi -=  'A';
272                         hi += 0xa;
273                 } else {
274                         goto on_error;
275                 }
276
277                 if (lo >= '0' && lo <= '9') {
278                         lo -= '0';
279                 } else if (lo >= 'a' && lo <= 'f') {
280                         lo -=  'a';
281                         lo += 0xa;
282                 } else if (lo >= 'A' && lo <= 'F') {
283                         lo -=  'A';
284                         lo += 0xa;
285                 } else {
286                         goto on_error;
287                 }
288
289                 *(p++) = (hi*0x10) + lo;
290         }
291
292         len /= 2;
293
294         if (len_p) *len_p = len;
295
296         buf[len] = '\0';
297
298         *err = NULL;
299         return buf;
300
301 on_error:
302         *err = g_strdup("Error parsing hex string");
303         return NULL;
304 }
305
306
307 static gboolean uat_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
308         struct _uat_dlg_data *dd    = (struct _uat_dlg_data *)user_data;
309         guint                 ncols = dd->uat->ncols;
310         uat_field_t          *f     = dd->uat->fields;
311         char                 *err   = NULL, *tmp_err = NULL;
312         guint                 colnum;
313
314         for ( colnum = 0; colnum < ncols; colnum++ ) {
315                 void *e = g_ptr_array_index(dd->entries, colnum);
316                 const char *text = NULL;
317                 char *text_free = NULL;
318                 guint len = 0;
319
320                 switch(f[colnum].mode) {
321                         case PT_TXTMOD_FILENAME:
322                         case PT_TXTMOD_DIRECTORYNAME:
323                                 text = text_free = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(e));
324                                 if (text) {
325                                         len = (unsigned) strlen(text);
326                                 } else {
327                                         text = "";
328                                         len = 0;
329                                 }
330                                 break;
331
332                         case PT_TXTMOD_STRING:
333                                 text = gtk_entry_get_text(GTK_ENTRY(e));
334                                 len = (unsigned) strlen(text);
335                                 break;
336                         case PT_TXTMOD_HEXBYTES: {
337                                 text = gtk_entry_get_text(GTK_ENTRY(e));
338
339                                 text_free = unhexbytes(text, (guint) strlen(text), &len, &err);
340                                 text = (const char *)text_free;
341
342                                 if (err) {
343                                         tmp_err = err;
344                                         err = g_strdup_printf("error in field '%s': %s", f[colnum].title, tmp_err);
345                                         g_free(tmp_err);
346                                         goto on_failure;
347                                 }
348
349                                 break;
350                         }
351                         case PT_TXTMOD_ENUM: {
352                                 gint idx = *(int*)e;
353                                 text = (idx >= 0) ? ((const value_string *)(f[colnum].fld_data))[idx].strptr : "";
354                                 len = (unsigned) strlen(text);
355                                 break;
356                         }
357                         case PT_TXTMOD_NONE: break;
358                         default:
359                                 g_assert_not_reached();
360                                 return FALSE;
361                 }
362
363                 if (f[colnum].cb.chk) {
364                         if (! f[colnum].cb.chk(dd->rec, text, len, f[colnum].cbdata.chk, f[colnum].fld_data, &err)) {
365                                 tmp_err = err;
366                                 err = g_strdup_printf("error in column '%s': %s", f[colnum].title, tmp_err);
367                                 g_free(tmp_err);
368                                 goto on_failure;
369                         }
370                 }
371
372                 f[colnum].cb.set(dd->rec, text, len, f[colnum].cbdata.set, f[colnum].fld_data);
373
374                 g_free(text_free);
375         }
376
377         if (dd->uat->update_cb) {
378                 if (!dd->uat->update_cb(dd->rec, &err)) {
379                         tmp_err = err;
380                         err = g_strdup_printf("error updating record: %s", tmp_err);
381                         g_free(tmp_err);
382                         goto on_failure;
383                 }
384         }
385
386         if (dd->is_new) {
387                 void *rec_tmp = dd->rec;
388                 dd->rec = uat_add_record(dd->uat, dd->rec, TRUE);
389
390                 if (dd->uat->free_cb) {
391                         dd->uat->free_cb(rec_tmp);
392                 }
393
394                 g_free(rec_tmp);
395         } else {
396                 uat_update_record(dd->uat, dd->rec, TRUE);
397         }
398
399         dd->uat->changed = TRUE;
400
401         set_buttons(dd->uat, dd->uat->rep ? dd->uat->rep->selected : -1);
402
403         if (dd->is_new) {
404                 append_row(dd->uat, dd->uat->raw_data->len - 1 );
405         } else {
406                 reset_row(dd->uat, dd->row);
407         }
408
409         g_ptr_array_free(dd->entries, TRUE);
410         window_destroy(GTK_WIDGET(dd->win));
411
412         if (dd->uat->rep)
413                 window_present(GTK_WIDGET(dd->uat->rep->window));
414
415         while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
416
417         g_free(dd);
418
419         return TRUE;
420 on_failure:
421
422         report_failure("%s", err);
423         g_free(err);
424         return FALSE;
425 }
426
427 static gboolean uat_cancel_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
428         struct _uat_dlg_data *dd = (struct _uat_dlg_data *)user_data;
429
430         if (dd->uat->rep)
431                 window_present(GTK_WIDGET(dd->uat->rep->window));
432
433         /* Reset the buttons */
434         set_buttons(dd->uat, dd->uat->rep ? dd->uat->rep->selected : -1);
435
436         if (dd->is_new) g_free(dd->rec);
437         g_ptr_array_free(dd->entries, TRUE);
438         window_destroy(GTK_WIDGET(dd->win));
439
440         while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
441
442         g_free(dd);
443
444         return TRUE;
445 }
446
447 static void fld_combo_box_changed_cb(GtkComboBox *combo_box, gpointer user_data) {
448         int *valptr = (int *)user_data;
449
450         *valptr = gtk_combo_box_get_active(combo_box);
451 }
452
453 static void uat_edit_dialog(uat_t *uat, gint row, gboolean copy) {
454         GtkWidget            *win, *main_grid, *main_vb, *bbox, *bt_cancel, *bt_ok;
455         struct _uat_dlg_data *dd = (struct _uat_dlg_data *)g_malloc(sizeof(struct _uat_dlg_data));
456         uat_field_t          *f  = uat->fields;
457         guint                 colnum;
458         gchar            *tmp_str;
459
460         /* Only allow a single operation at a time, prevents bug 9129 */
461         limit_buttons(uat);
462
463         dd->entries = g_ptr_array_new();
464         tmp_str = g_strdup_printf("%s: %s", uat->name, (row == -1 ? "New" : "Edit"));
465         dd->win = dlg_conf_window_new(tmp_str);
466         g_free(tmp_str);
467         dd->uat = uat;
468         if (copy && row >= 0) {
469           dd->rec = g_malloc0(uat->record_size);
470           if (uat->copy_cb) {
471             uat->copy_cb (dd->rec, UAT_INDEX_PTR(uat, row), uat->record_size);
472           }
473           else {
474             /* According to documentation of uat_copy_cb_t memcpy should be used if uat->copy_cb is NULL */
475             memcpy(dd->rec, UAT_INDEX_PTR(uat, row), uat->record_size);
476           }
477           dd->is_new = TRUE;
478         } else if (row >= 0) {
479           dd->rec = UAT_INDEX_PTR(uat, row);
480           dd->is_new = FALSE;
481         } else {
482           dd->rec = g_malloc0(uat->record_size);
483           dd->is_new = TRUE;
484         }
485         dd->row = row;
486         dd->tobe_freed = g_ptr_array_new();
487
488         win = dd->win;
489
490         gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
491         gtk_window_resize(GTK_WINDOW(win), 400, 30*(uat->ncols+2));
492
493         main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
494         gtk_container_add(GTK_CONTAINER(win), main_vb);
495         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
496
497         main_grid = ws_gtk_grid_new();
498         gtk_box_pack_start(GTK_BOX(main_vb), main_grid, FALSE, FALSE, 0);
499         ws_gtk_grid_set_row_spacing(GTK_GRID(main_grid), 5);
500         ws_gtk_grid_set_column_spacing(GTK_GRID(main_grid), 10);
501
502         bbox = dlg_button_row_new(GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
503         gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
504
505         bt_ok = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
506         g_signal_connect(bt_ok, "clicked", G_CALLBACK(uat_dlg_cb), dd);
507
508         bt_cancel = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
509         g_signal_connect(bt_cancel, "clicked", G_CALLBACK(uat_cancel_dlg_cb), dd);
510         window_set_cancel_button(win, bt_cancel, NULL);
511
512         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
513                 GtkWidget *entry, *label, *event_box;
514                 char *text = fld_tostr(dd->rec, &(f[colnum]));
515                 char *label_text;
516                 gchar *fc_filename;
517
518                 event_box = gtk_event_box_new();
519
520                 label_text = g_strdup_printf("%s:", f[colnum].title);
521                 label = gtk_label_new(label_text);
522                 if (f[colnum].desc != NULL)
523                         gtk_widget_set_tooltip_text(event_box, f[colnum].desc);
524
525                 g_free(label_text);
526                 gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
527                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), event_box, 0, colnum, 1, 1);
528                 gtk_container_add(GTK_CONTAINER(event_box), label);
529
530                 switch(f[colnum].mode) {
531                         case PT_TXTMOD_FILENAME:
532                         case PT_TXTMOD_DIRECTORYNAME:
533                                 entry = gtk_file_chooser_button_new(f[colnum].desc,
534                                                                     (f[colnum].mode == PT_TXTMOD_FILENAME) ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
535                                 if (! dd->is_new || copy) {
536                                         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(entry), text);
537                                 }
538
539                                 /*
540                                  * Some versions of GTK+ will crash if fc_filename is NULL.
541                                  * Make sure we have a valid location set.
542                                  */
543                                 fc_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(entry));
544                                 if (!fc_filename) {
545                                         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(entry), get_datafile_dir());
546                                 }
547                                 g_free(fc_filename);
548
549                                 g_ptr_array_add(dd->entries, entry);
550                                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), entry, 1, colnum, 1, 1);
551                                 break;
552
553                         case PT_TXTMOD_NONE:
554                         case PT_TXTMOD_STRING:
555                         case PT_TXTMOD_HEXBYTES:
556                                 entry = gtk_entry_new();
557                                 if (! dd->is_new || copy) {
558                                         gtk_entry_set_text(GTK_ENTRY(entry), text);
559                                 }
560                                 g_ptr_array_add(dd->entries, entry);
561                                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), entry, 1, colnum, 1, 1);
562                                 if (f[colnum].mode != PT_TXTMOD_NONE)
563                                         dlg_set_activate(entry, bt_ok);
564                                 else
565                                         gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
566                                 break;
567
568                         case PT_TXTMOD_ENUM: {
569                                 GtkWidget *combo_box;
570                                 int idx;
571                                 const value_string *enum_vals = (const value_string *)f[colnum].fld_data;
572                                 int *valptr = (int *)g_malloc(sizeof(int));     /* A place to store the index of the    */
573                                                                         /*  "active" fld_data array entry       */
574                                                                         /* -1 means "nothing selected (active)" */
575                                 combo_box = gtk_combo_box_text_new();
576                                 *valptr = -1;
577                                 for (idx = 0; enum_vals[idx].strptr != NULL; idx++) {
578                                         const char *str = enum_vals[idx].strptr;
579                                          gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(combo_box), str);
580
581                                         if ( g_str_equal(str, text) ) {
582                                                 *valptr = idx;
583                                         }
584                                 }
585
586                                 g_ptr_array_add(dd->entries, valptr);
587                                 g_ptr_array_add(dd->tobe_freed, valptr);
588
589                                 if (*valptr != -1)
590                                         gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), *valptr);
591
592                                 g_signal_connect(combo_box, "changed", G_CALLBACK(fld_combo_box_changed_cb), valptr);
593                                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), combo_box, 1, colnum, 1, 1);
594
595                                 break;
596                         }
597                         default:
598                                 g_assert_not_reached();
599                                 return;
600                 }
601                 g_free(text);
602         }
603
604         gtk_widget_grab_default(bt_ok);
605         gtk_widget_show_all(win);
606 }
607
608 struct _uat_del {
609         GtkWidget *win;
610         uat_t     *uat;
611         gint       idx;
612 };
613
614 static void uat_del_cb(GtkButton *button _U_, gpointer u) {
615         struct _uat_del *ud = (struct _uat_del *)u;
616         GtkTreeIter      iter;
617         GtkTreePath     *path;
618
619         uat_remove_record_idx(ud->uat, ud->idx);
620
621         if (ud->uat->rep) {
622                 path = gtk_tree_path_new_from_indices(ud->idx, -1);
623                 if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(ud->uat->rep->list_store), &iter, path)) {
624                         gtk_list_store_remove(ud->uat->rep->list_store, &iter);
625                 }
626         }
627
628         ud->uat->changed = TRUE;
629         set_buttons(ud->uat, -1);
630
631         window_destroy(GTK_WIDGET(ud->win));
632
633         if (ud->uat->rep)
634                 window_present(GTK_WIDGET(ud->uat->rep->window));
635
636         g_free(ud);
637 }
638
639 static void uat_cancel_del_cb(GtkButton *button _U_, gpointer u) {
640         struct _uat_del *ud = (struct _uat_del *)u;
641
642         window_destroy(GTK_WIDGET(ud->win));
643
644         /* Reset the buttons */
645         set_buttons(ud->uat, ud->uat->rep ? ud->uat->rep->selected : -1);
646
647         if (ud->uat->rep)
648                 window_present(GTK_WIDGET(ud->uat->rep->window));
649         g_free(ud);
650 }
651
652 static void uat_del_dlg(uat_t *uat, int idx) {
653         GtkWidget       *win, *main_grid, *main_vb, *bbox, *bt_cancel, *bt_ok;
654         uat_field_t     *f   = uat->fields;
655         guint            colnum;
656         void            *rec = UAT_INDEX_PTR(uat, idx);
657         gchar       *tmp_str;
658
659         struct _uat_del *ud  = (struct _uat_del *)g_malloc(sizeof(struct _uat_del));
660
661         /* Only allow a single operation at a time, prevents bug 9129 */
662         limit_buttons(uat);
663
664         ud->uat = uat;
665         ud->idx = idx;
666         tmp_str = g_strdup_printf("%s: Confirm Delete", uat->name);
667         ud->win = win = dlg_conf_window_new(tmp_str);
668         g_free(tmp_str);
669
670         gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
671         gtk_window_resize(GTK_WINDOW(win), 400, 25*(uat->ncols+2));
672
673         main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 5, FALSE);
674         gtk_container_add(GTK_CONTAINER(win), main_vb);
675         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
676
677         main_grid = ws_gtk_grid_new();
678         gtk_box_pack_start(GTK_BOX(main_vb), main_grid, FALSE, FALSE, 0);
679         ws_gtk_grid_set_row_spacing(GTK_GRID(main_grid), 10);
680         ws_gtk_grid_set_column_spacing(GTK_GRID(main_grid), 15);
681
682         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
683                 GtkWidget *label;
684                 char *text = fld_tostr(rec, &(f[colnum]));
685
686                 tmp_str = g_strdup_printf("%s:", f[colnum].title);
687                 label = gtk_label_new(tmp_str);
688                 gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
689                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), label, 0, colnum, 1, 1);
690                 g_free(tmp_str);
691
692                 label = gtk_label_new(text);
693                 gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
694                 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid), label, 1, colnum, 1, 1);
695                 g_free(text);
696         }
697
698         bbox = dlg_button_row_new(GTK_STOCK_CANCEL, GTK_STOCK_DELETE, NULL);
699         gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
700
701         bt_ok = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_DELETE);
702         g_signal_connect(bt_ok, "clicked", G_CALLBACK(uat_del_cb), ud);
703
704         bt_cancel = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
705         g_signal_connect(bt_cancel, "clicked", G_CALLBACK(uat_cancel_del_cb), ud);
706         window_set_cancel_button( win, bt_cancel, NULL);
707
708         gtk_widget_show_all(win);
709 }
710
711 static void uat_new_cb(GtkButton *button _U_, gpointer u) {
712         uat_t *uat = (uat_t *)u;
713
714         if (! uat->rep) return;
715
716         uat_edit_dialog(uat, -1, FALSE);
717 }
718
719 static void uat_edit_cb(GtkWidget *button _U_, gpointer u) {
720         uat_t *uat = (uat_t *)u;
721
722         if (! uat->rep) return;
723
724         uat_edit_dialog(uat, uat->rep->selected, FALSE);
725 }
726
727 static void uat_copy_cb(GtkWidget *button _U_, gpointer u) {
728         uat_t *uat = (uat_t *)u;
729
730         if (! uat->rep) return;
731
732         uat_edit_dialog(uat, uat->rep->selected, TRUE);
733 }
734
735 static void uat_double_click_cb(GtkWidget *tv, GtkTreePath *path _U_, GtkTreeViewColumn *column _U_, gpointer u) {
736         uat_edit_cb(tv, u);
737 }
738
739 static void uat_delete_cb(GtkButton *button _U_, gpointer u) {
740         uat_t *uat = (uat_t *)u;
741
742         if (! uat->rep) return;
743
744         uat_del_dlg(uat, uat->rep->selected);
745 }
746
747 static gboolean uat_window_delete_event_cb(GtkWindow *w _U_, GdkEvent *e _U_, gpointer u) {
748         uat_t *uat = (uat_t *)u;
749
750         if (uat->rep) {
751                 void *rep = uat->rep;
752
753                 g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
754                 g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
755
756                 gtk_widget_destroy(uat->rep->window);
757
758                 uat->rep = NULL;
759                 g_free(rep);
760         }
761         return TRUE;
762 }
763
764 static void uat_up_cb(GtkButton *button _U_, gpointer u) {
765         uat_t *uat = (uat_t *)u;
766         gint   row = uat->rep->selected;
767
768         g_assert(row > 0);
769
770         uat_swap(uat, row, row-1);
771         tree_view_list_store_move_selection(uat->rep->list, TRUE);
772
773         uat->changed = TRUE;
774
775         row -= 1;
776         uat->rep->selected = row;
777         set_buttons(uat, row);
778 }
779
780 static void uat_down_cb(GtkButton *button _U_, gpointer u) {
781         uat_t *uat = (uat_t *)u;
782         gint   row = uat->rep->selected;
783
784         g_assert(row >= 0 && (guint) row < uat->raw_data->len - 1);
785
786         uat_swap(uat, row, row+1);
787         tree_view_list_store_move_selection(uat->rep->list, FALSE);
788
789         uat->changed = TRUE;
790
791         row += 1;
792         uat->rep->selected = row;
793         set_buttons(uat, row);
794 }
795
796 static void uat_apply_changes(uat_t *uat) {
797         if (uat->flags & UAT_AFFECTS_FIELDS) {
798                 /* Recreate list with new fields and redissect packets */
799                 main_fields_changed ();
800         } else {
801                 if (uat->flags & UAT_AFFECTS_DISSECTION) {
802                         /* Just redissect packets if we have any */
803                         if (cfile.state != FILE_CLOSED) {
804                                 redissect_packets ();
805                                 redissect_all_packet_windows ();
806                         }
807                 }
808         }
809 }
810
811 static void uat_cancel_cb(GtkWidget *button _U_, gpointer u) {
812         uat_t *uat = (uat_t *)u;
813         gchar *err = NULL;
814
815         if (uat->changed) {
816                 uat_clear(uat);
817                 if (!uat_load(uat, &err)) {
818                         report_failure("Error while loading %s: %s", uat->name, err);
819                         g_free(err);
820                 }
821
822                 uat_apply_changes (uat);
823         }
824
825         g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
826         g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
827         gtk_widget_destroy(uat->rep->window);
828         g_free(uat->rep);
829         uat->rep = NULL;
830 }
831
832 static void uat_apply_cb(GtkButton *button _U_, gpointer u) {
833         uat_t *uat = (uat_t *)u;
834
835         if (uat->changed) {
836                 if (uat->post_update_cb) uat->post_update_cb();
837                 uat_apply_changes (uat);
838         }
839 }
840
841 static void uat_ok_cb(GtkButton *button _U_, gpointer u) {
842         uat_t *uat = (uat_t *)u;
843         gchar *err = NULL;
844
845         if (uat->changed) {
846                 if (!uat_save(uat, &err)) {
847                         report_failure("Error while saving %s: %s", uat->name, err);
848                         g_free(err);
849                 }
850
851                 if (uat->post_update_cb) uat->post_update_cb();
852                 uat_apply_changes (uat);
853         }
854
855         g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
856         g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
857         gtk_widget_destroy(uat->rep->window);
858         g_free(uat->rep);
859         uat->rep = NULL;
860 }
861
862 static void uat_clear_cb(GtkButton *button _U_, gpointer u) {
863         uat_t *uat = (uat_t *)u;
864
865         gtk_list_store_clear(uat->rep->list_store);
866         uat_clear(uat);
867         uat->changed = TRUE;
868 }
869
870 static void uat_refresh_cb(GtkButton *button _U_, gpointer u) {
871         uat_t *uat = (uat_t *)u;
872         gchar *err = NULL;
873         guint  i;
874         gboolean success;
875
876         uat_clear_cb(button, u);
877
878         uat->from_global = TRUE;
879         success = uat_load(uat, &err);
880         uat->from_global = FALSE;
881         uat->changed = TRUE;
882
883         if (!success) {
884                 report_failure("Error while loading %s: %s", uat->name, err);
885                 g_free(err);
886         }
887
888         for (i = 0 ; i < uat->raw_data->len; i++) {
889                 append_row(uat, i);
890         }
891 }
892
893
894 static void remember_selected_row(GtkWidget *w _U_, gpointer u) {
895         uat_t *uat = (uat_t *)u;
896         gint   row;
897
898         row = tree_view_list_store_get_selected_row(uat->rep->list);
899         uat->rep->selected = row;
900
901         gtk_widget_set_sensitive (uat->rep->bt_edit, TRUE);
902         gtk_widget_set_sensitive (uat->rep->bt_copy, uat->copy_cb ? TRUE : FALSE);
903         gtk_widget_set_sensitive(uat->rep->bt_delete, TRUE);
904
905         set_buttons(uat, row);
906 }
907
908 static void uat_yessave_cb(GtkWindow *w _U_, void *u) {
909         uat_t *uat = (uat_t *)u;
910         gchar *err = NULL;
911
912         window_delete_event_cb(uat->rep->unsaved_window, NULL, NULL);
913
914         if (!uat_save(uat, &err)) {
915                 report_failure("Error while saving %s: %s", uat->name, err);
916                 g_free(err);
917         }
918
919         g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
920         g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
921         window_destroy(uat->rep->window);
922
923         g_free(uat->rep);
924         uat->rep = NULL;
925 }
926
927
928 static void uat_nosave_cb(GtkWindow *w _U_, void *u) {
929         uat_t *uat = (uat_t *)u;
930
931         window_delete_event_cb(uat->rep->unsaved_window, NULL, NULL);
932         g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
933         g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
934         window_destroy(uat->rep->window);
935
936         g_free(uat->rep);
937         uat->rep = NULL;
938 }
939
940 static gboolean unsaved_dialog(GtkWindow *w _U_, GdkEvent *e _U_, gpointer u) {
941         GtkWidget *win, *vbox, *label, *bbox;
942         GtkWidget *yes_bt, *no_bt;
943         gchar     *message;
944         uat_t     *uat = (uat_t *)u;
945
946         if (uat->rep->unsaved_window) {
947                 window_present(uat->rep->unsaved_window);
948                 return TRUE;
949         }
950
951         uat->rep->unsaved_window = win = dlg_conf_window_new("Discard Changes?");
952         gtk_window_set_default_size(GTK_WINDOW(win), 360, 140);
953
954         gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
955         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 12, FALSE);
956         gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
957         gtk_container_add(GTK_CONTAINER(win), vbox);
958
959         message  = g_strdup_printf("Changes to '%s' are not being saved.\n"
960                 "Do you want to save '%s'?", uat->name, uat->name);
961
962         label = gtk_label_new(message);
963         g_free(message);
964
965         bbox = dlg_button_row_new(GTK_STOCK_YES, GTK_STOCK_NO, NULL);
966
967         yes_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_YES);
968         no_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_NO);
969
970         g_signal_connect(no_bt, "clicked", G_CALLBACK(uat_nosave_cb), uat);
971         g_signal_connect(yes_bt, "clicked", G_CALLBACK(uat_yessave_cb), uat);
972
973         gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
974         gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
975
976         gtk_widget_show_all(win);
977         window_present(win);
978
979         return TRUE;
980 }
981
982 static void uat_help_cb(GtkWidget *w _U_, gpointer u) {
983         gchar *help_page, *url;
984
985         help_page = g_strdup_printf("%s.html", ((uat_t*)u)->help);
986         url = user_guide_url(help_page);
987         if (url) {
988                 browser_open_url(url);
989         }
990         g_free(help_page);
991         g_free(url);
992 }
993
994 static GtkWidget *uat_window(void *u) {
995         uat_t             *uat = (uat_t *)u;
996         uat_field_t       *f   = uat->fields;
997         uat_rep_t         *rep;
998         guint              i;
999         guint              colnum;
1000         GType             *col_types;
1001         GtkWidget         *hbox, *vbox, *move_hbox, *edit_hbox, *refresh_hbox;
1002         GtkTreeViewColumn *column;
1003         GtkCellRenderer   *renderer;
1004         GtkTreeSelection  *selection;
1005         gchar             *global_fname;
1006         gboolean           global_file_exists;
1007
1008         if (uat->rep) {
1009                 window_present(uat->rep->window);
1010                 return uat->rep->window;
1011         } else {
1012                 uat->rep = rep = (uat_rep_t *)g_malloc0(sizeof(uat_rep_t));
1013         }
1014
1015         global_fname = get_datafile_path(uat->filename);
1016         global_file_exists = file_exists(global_fname);
1017         g_free (global_fname);
1018
1019         rep->window = dlg_conf_window_new(uat->name);
1020
1021         gtk_window_set_default_size(GTK_WINDOW(rep->window), 720, 512);
1022         gtk_window_set_position(GTK_WINDOW(rep->window), GTK_WIN_POS_CENTER_ON_PARENT);
1023
1024         gtk_container_set_border_width(GTK_CONTAINER(rep->window), 6);
1025
1026         rep->vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 12, FALSE);
1027         gtk_container_set_border_width(GTK_CONTAINER(rep->vbox), 6);
1028         gtk_container_add(GTK_CONTAINER(rep->window), rep->vbox);
1029
1030         hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12, FALSE);
1031         gtk_container_set_border_width(GTK_CONTAINER(hbox), 6);
1032         gtk_box_pack_start(GTK_BOX(rep->vbox), hbox, TRUE, TRUE, 0);
1033
1034         vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 12, FALSE);
1035         gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
1036         gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
1037
1038         rep->scrolledwindow = scrolled_window_new(NULL, NULL);
1039         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(rep->scrolledwindow), GTK_SHADOW_IN);
1040
1041         col_types = (GType *)g_malloc(sizeof(GType)  *uat->ncols);
1042         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
1043                 col_types[colnum] = G_TYPE_STRING;
1044         }
1045         rep->list_store = gtk_list_store_newv(uat->ncols, col_types);
1046         g_free(col_types);
1047
1048         rep->list = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(rep->list_store))); /*uat->ncols */
1049         gtk_container_add(GTK_CONTAINER(rep->scrolledwindow), GTK_WIDGET(rep->list));
1050         gtk_box_pack_start(GTK_BOX(hbox), rep->scrolledwindow, TRUE, TRUE, 0);
1051
1052         selection = gtk_tree_view_get_selection(rep->list);
1053         gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
1054         rep->selected = -1;
1055
1056         for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
1057                 renderer = gtk_cell_renderer_text_new();
1058                 column = gtk_tree_view_column_new_with_attributes(f[colnum].title,
1059                         renderer, "text", colnum, NULL);
1060                 gtk_tree_view_column_set_resizable (column, TRUE);
1061                 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1062                 gtk_tree_view_append_column (rep->list, column);
1063                 if (f[colnum].desc != NULL)
1064                         gtk_widget_set_tooltip_text(gtk_tree_view_column_get_button(column), f[colnum].desc);
1065         }
1066
1067         for ( i = 0 ; i < uat->raw_data->len; i++ ) {
1068                 append_row(uat, i);
1069         }
1070
1071         if (uat->help) {
1072                 GtkWidget *help_btn;
1073                 rep->bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
1074                 help_btn = (GtkWidget *)g_object_get_data(G_OBJECT(rep->bbox), GTK_STOCK_HELP);
1075                 g_signal_connect(help_btn, "clicked", G_CALLBACK(uat_help_cb), uat);
1076         } else {
1077
1078                 rep->bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
1079         }
1080
1081         move_hbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
1082         gtk_box_pack_start(GTK_BOX(vbox), move_hbox, TRUE, FALSE, 0);
1083
1084         edit_hbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
1085         gtk_box_pack_start(GTK_BOX(vbox), edit_hbox, TRUE, FALSE, 0);
1086
1087         refresh_hbox = gtk_button_box_new(GTK_ORIENTATION_VERTICAL);
1088         gtk_box_pack_end(GTK_BOX(vbox), refresh_hbox, TRUE, FALSE, 0);
1089
1090
1091         rep->bt_up = ws_gtk_button_new_from_stock(GTK_STOCK_GO_UP);
1092         gtk_widget_set_tooltip_text(rep->bt_up, "Move selected entry up");
1093
1094         rep->bt_down = ws_gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
1095         gtk_widget_set_tooltip_text(rep->bt_down, "Move selected entry down");
1096
1097         gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_up, TRUE, FALSE, 5);
1098         gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_down, TRUE, FALSE, 5);
1099
1100
1101         rep->bt_new = ws_gtk_button_new_from_stock(GTK_STOCK_NEW);
1102         gtk_widget_set_tooltip_text(rep->bt_new, "Create a new entry");
1103
1104         rep->bt_edit = ws_gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
1105         gtk_widget_set_tooltip_text(rep->bt_edit, "Edit selected entry");
1106
1107         rep->bt_copy = ws_gtk_button_new_from_stock(GTK_STOCK_COPY);
1108         gtk_widget_set_tooltip_text(rep->bt_copy, "Copy selected entry");
1109
1110         rep->bt_delete = ws_gtk_button_new_from_stock(GTK_STOCK_DELETE);
1111         gtk_widget_set_tooltip_text(rep->bt_delete, "Delete selected entry");
1112
1113         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_new, TRUE, FALSE, 5);
1114         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_edit, TRUE, FALSE, 5);
1115         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_copy, TRUE, FALSE, 5);
1116         gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_delete, TRUE, FALSE, 5);
1117
1118         rep->bt_refresh = ws_gtk_button_new_from_stock(GTK_STOCK_REFRESH);
1119         gtk_widget_set_tooltip_text(rep->bt_refresh, "Refresh from system defaults");
1120
1121         rep->bt_clear = ws_gtk_button_new_from_stock(GTK_STOCK_CLEAR);
1122         gtk_widget_set_tooltip_text(rep->bt_clear, "Delete all entries");
1123
1124         gtk_box_pack_end(GTK_BOX(refresh_hbox), rep->bt_refresh, TRUE, FALSE, 5);
1125         gtk_box_pack_end(GTK_BOX(refresh_hbox), rep->bt_clear, TRUE, FALSE, 5);
1126
1127
1128         rep->bt_apply = (GtkWidget *)g_object_get_data(G_OBJECT(rep->bbox), GTK_STOCK_APPLY);
1129         rep->bt_cancel = (GtkWidget *)g_object_get_data(G_OBJECT(rep->bbox), GTK_STOCK_CANCEL);
1130         rep->bt_ok = (GtkWidget *)g_object_get_data(G_OBJECT(rep->bbox), GTK_STOCK_OK);
1131
1132         gtk_box_pack_end(GTK_BOX(rep->vbox), rep->bbox, FALSE, FALSE, 0);
1133
1134         gtk_widget_set_sensitive (rep->bt_up, FALSE);
1135         gtk_widget_set_sensitive (rep->bt_down, FALSE);
1136         gtk_widget_set_sensitive (rep->bt_edit, FALSE);
1137         gtk_widget_set_sensitive (rep->bt_copy, FALSE);
1138         gtk_widget_set_sensitive (rep->bt_delete, FALSE);
1139         gtk_widget_set_sensitive (rep->bt_refresh, global_file_exists);
1140
1141         g_signal_connect(rep->list, "row-activated", G_CALLBACK(uat_double_click_cb), uat);
1142         g_signal_connect(selection, "changed", G_CALLBACK(remember_selected_row), uat);
1143
1144
1145         g_signal_connect(rep->bt_new, "clicked", G_CALLBACK(uat_new_cb), uat);
1146         g_signal_connect(rep->bt_edit, "clicked", G_CALLBACK(uat_edit_cb), uat);
1147         g_signal_connect(rep->bt_copy, "clicked", G_CALLBACK(uat_copy_cb), uat);
1148         g_signal_connect(rep->bt_delete, "clicked", G_CALLBACK(uat_delete_cb), uat);
1149
1150         g_signal_connect(rep->bt_refresh, "clicked", G_CALLBACK(uat_refresh_cb), uat);
1151         g_signal_connect(rep->bt_clear, "clicked", G_CALLBACK(uat_clear_cb), uat);
1152
1153         g_signal_connect(rep->bt_up, "clicked", G_CALLBACK(uat_up_cb), uat);
1154         g_signal_connect(rep->bt_down, "clicked", G_CALLBACK(uat_down_cb), uat);
1155
1156         g_signal_connect(rep->bt_apply, "clicked", G_CALLBACK(uat_apply_cb), uat);
1157         g_signal_connect(rep->bt_cancel, "clicked", G_CALLBACK(uat_cancel_cb), uat);
1158         g_signal_connect(rep->bt_ok, "clicked", G_CALLBACK(uat_ok_cb), uat);
1159
1160         window_set_cancel_button(rep->window, rep->bt_cancel, NULL);  /*set esc to activate cancel button */
1161
1162         if (uat->changed) {
1163                 g_signal_connect(GTK_WINDOW(rep->window), "delete_event", G_CALLBACK(unsaved_dialog), uat);
1164                 g_signal_connect(GTK_WINDOW(rep->window), "destroy", G_CALLBACK(unsaved_dialog), uat);
1165         } else {
1166                 g_signal_connect(GTK_WINDOW(rep->window), "delete_event", G_CALLBACK(uat_window_delete_event_cb), uat);
1167                 g_signal_connect(GTK_WINDOW(rep->window), "destroy", G_CALLBACK(uat_window_delete_event_cb), uat);
1168         }
1169
1170         gtk_widget_grab_focus(GTK_WIDGET(rep->list));
1171
1172         gtk_widget_show_all(rep->window);
1173         window_present(rep->window);
1174
1175         return rep->window;
1176 }
1177
1178 void uat_window_cb(GtkWidget *u _U_, void *uat) {
1179         uat_window(uat);
1180 }
1181
1182 /*
1183  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1184  *
1185  * Local variables:
1186  * c-basic-offset: 8
1187  * tab-width: 8
1188  * indent-tabs-mode: t
1189  * End:
1190  *
1191  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1192  * :indentSize=8:tabSize=8:noTabs=false:
1193  */