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