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