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