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