Fix some simple cases of GTK2 deprecated API usage by using a renamed or equivalent API
[obnox/wireshark/wip.git] / gtk / profile_dlg.c
1 /* profile_dlg.c
2  * Dialog box for profiles editing
3  * Stig Bjørlykke <stig@bjorlykke.org>, 2008
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <string.h>
31
32 #include <gtk/gtk.h>
33 #include <gdk/gdkkeysyms.h>
34
35 #include <epan/filesystem.h>
36 #include <epan/prefs.h>
37
38 #include "../simple_dialog.h"
39 #include <wsutil/file_util.h>
40
41 #include "gtk/main.h"
42 #include "gtk/profile_dlg.h"
43 #include "gtk/dlg_utils.h"
44 #include "gtk/gui_utils.h"
45 #include "gtk/gtkglobals.h"
46 #include "gtk/help_dlg.h"
47
48
49 #define E_PROF_PROFILE_L_KEY        "profile_profile_l"
50 #define E_PROF_COPY_BT_KEY          "profile_copy_bt"
51 #define E_PROF_DEL_BT_KEY           "profile_del_bt"
52 #define E_PROF_NAME_TE_KEY          "profile_name_te"
53
54 static GtkWidget *global_profile_w = NULL;
55 static GList *current_profiles = NULL;
56 static GList *edited_profiles = NULL;
57
58 #define PROF_STAT_DEFAULT  1
59 #define PROF_STAT_EXISTS   2
60 #define PROF_STAT_NEW      3
61 #define PROF_STAT_CHANGED  4
62 #define PROF_STAT_COPY     5
63
64 typedef struct {
65   char *name;           /* profile name */
66   char *reference;      /* profile reference */
67   int   status;
68 } profile_def;
69
70 static GList *
71 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status)
72 {
73     profile_def *profile;
74
75     profile = (profile_def *) g_malloc(sizeof(profile_def));
76     profile->name = g_strdup(profilename);
77     profile->reference = g_strdup(reference);
78     profile->status = status;
79     return g_list_append(fl, profile);
80 }
81
82 static GList *
83 remove_profile_entry(GList *fl, GList *fl_entry)
84 {
85   profile_def *profile;
86
87   profile = (profile_def *) fl_entry->data;
88   g_free(profile->name);
89   g_free(profile->reference);
90   g_free(profile);
91   return g_list_remove_link(fl, fl_entry);
92 }
93
94 static GList *
95 add_to_profile_list(const char *name, const char *expression, int status)
96 {
97   edited_profiles = add_profile_entry(edited_profiles, name, expression, status);
98
99   return g_list_last(edited_profiles);
100 }
101
102 static void
103 remove_from_profile_list(GList *fl_entry)
104 {
105   edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
106 }
107
108 static void
109 empty_profile_list(gboolean edit_list)
110 {
111   GList **flpp;
112
113   if (edit_list) {
114     flpp = &edited_profiles;
115
116     while(*flpp) {
117       *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
118     }
119
120     g_assert(g_list_length(*flpp) == 0);
121   }
122
123   flpp = &current_profiles;
124
125   while(*flpp) {
126     *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
127   }
128
129   g_assert(g_list_length(*flpp) == 0);
130 }
131
132 static void
133 copy_profile_list(void)
134 {
135     GList      *flp_src;
136     profile_def *profile;
137
138     flp_src = edited_profiles;
139
140     /* throw away the "old" destination list - a NULL list is ok here */
141     empty_profile_list(FALSE);
142
143     /* copy the list entries */
144     while(flp_src) {
145         profile = (flp_src)->data;
146
147         current_profiles = add_profile_entry(current_profiles, profile->name,
148                                              profile->reference, profile->status);
149         flp_src = g_list_next(flp_src);
150     }
151 }
152
153
154 static GtkTreeIter *
155 fill_list(GtkWidget *main_w)
156 {
157   ETH_DIR       *dir;             /* scanned directory */
158   ETH_DIRENT    *file;            /* current file */
159   GList         *fl_entry;
160   profile_def   *profile;
161   GtkTreeView   *profile_l;
162   GtkListStore  *store;
163   GtkTreeIter    iter, *l_select = NULL;
164   const gchar   *profile_name = get_profile_name ();
165   const gchar   *profiles_dir, *name;
166   gchar         *filename;
167
168   profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
169   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
170
171   fl_entry = add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT);
172   gtk_list_store_append(store, &iter);
173   gtk_list_store_set(store, &iter, 0, DEFAULT_PROFILE, 1, fl_entry, -1);
174   if (strcmp (profile_name, DEFAULT_PROFILE)==0) {
175     l_select = g_memdup(&iter, sizeof(iter));
176   }
177
178   /* fill in data */
179   profiles_dir = get_profiles_dir();
180   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
181     while ((file = ws_dir_read_name(dir)) != NULL) {
182       name = ws_dir_get_name(file);
183       filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
184
185       if (test_for_directory(filename) == EISDIR) {
186         fl_entry = add_to_profile_list(name, name, PROF_STAT_EXISTS);
187         profile    = (profile_def *) fl_entry->data;
188         gtk_list_store_append(store, &iter);
189         gtk_list_store_set(store, &iter, 0, profile->name, 1, fl_entry, -1);
190
191         if (profile->name) {
192           if (strcmp(profile_name, profile->name) == 0) {
193             /*
194              * XXX - We're assuming that we can just copy a GtkTreeIter
195              * and use it later without any crashes.  This may not be a
196              * valid assumption.
197              */
198             l_select = g_memdup(&iter, sizeof(iter));
199           }
200         }
201       }
202       g_free (filename);
203     }
204     ws_dir_close (dir);
205   }
206
207   /* Make the current list an the edited list equal */
208   copy_profile_list ();
209
210   return l_select;
211 }
212
213 static gboolean
214 profile_is_invalid_name(gchar *name)
215 {
216   gchar  *message = NULL;
217
218 #ifdef _WIN32
219   char *invalid_dir_char = "\\/:*?\"<>|";
220   gboolean invalid = FALSE;
221   int i;
222
223   for (i = 0; i < 9; i++) {
224     if (strchr(name, invalid_dir_char[i])) {
225       /* Invalid character in directory */
226       invalid = TRUE;
227     }
228   }
229   if (name[0] == '.' || name[strlen(name)-1] == '.') {
230     /* Profile name cannot start or end with period */
231     invalid = TRUE;
232   }
233   if (invalid) {
234     message = g_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
235                               "   \\ / : * ? \" &lt; &gt; |");
236   }
237 #else
238   if (strchr(name, '/')) {
239     /* Invalid character in directory */
240     message = g_strdup_printf("contain the '/' character.");
241   }
242 #endif
243
244   if (message) {
245     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "A profile name cannot %s\nProfiles unchanged.", message);
246     g_free(message);
247     return TRUE;
248   }
249
250   return FALSE;
251 }
252
253 static void
254 profile_select(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
255 {
256   GList            *fl_entry;
257   profile_def      *profile;
258   GtkTreeSelection *sel;
259   GtkTreeModel     *model;
260   GtkTreeIter       iter;
261
262   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
263
264   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
265     gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
266     if (fl_entry) {
267       profile = (profile_def *) fl_entry->data;
268       if (profile_exists (profile->name)) {
269         /* The new profile exists, change */
270         change_configuration_profile (profile->name);
271       } else if (!profile_exists (get_profile_name())) {
272         /* The new profile does not exist, and the previous profile has
273            been deleted.  Change to the default profile */
274         change_configuration_profile (NULL);
275       }
276     }
277   }
278
279   if (destroy) {
280     /*
281      * Destroy the profile dialog box.
282      */
283     empty_profile_list (TRUE);
284     window_destroy(main_w);
285   }
286 }
287
288 static void
289 profile_apply(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
290 {
291   char        *pf_dir_path, *pf_dir_path2;
292   GList       *fl1, *fl2;
293   profile_def *profile1, *profile2;
294   gboolean     found;
295
296   /* First validate all profile names */
297   fl1 = g_list_first(edited_profiles);
298   while (fl1) {
299     found = FALSE;
300     profile1 = (profile_def *) fl1->data;
301     g_strstrip(profile1->name);
302     if (profile_is_invalid_name(profile1->name)) {
303       return;
304     }
305     fl1 = g_list_next(fl1);
306   }
307
308   /* Then create new and rename changed */
309   fl1 = g_list_first(edited_profiles);
310   while (fl1) {
311     found = FALSE;
312     profile1 = (profile_def *) fl1->data;
313     g_strstrip(profile1->name);
314     if (profile1->status == PROF_STAT_NEW) {
315       /* We do not create a directory for the default profile */
316       if (strcmp(profile1->name, DEFAULT_PROFILE)!=0) {
317         if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
318           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
319                         "Can't create directory\n\"%s\":\n%s.",
320                         pf_dir_path, strerror(errno));
321
322           g_free(pf_dir_path);
323         }
324         profile1->status = PROF_STAT_EXISTS;
325         if (profile1->reference) {
326           g_free (profile1->reference);
327         }
328         profile1->reference = g_strdup(profile1->name);
329       }
330     } else if (profile1->status == PROF_STAT_CHANGED) {
331       if (strcmp(profile1->reference, profile1->name)!=0) {
332         /* Rename old profile directory to new */
333         if (rename_persconffile_profile(profile1->reference, profile1->name,
334                                         &pf_dir_path, &pf_dir_path2) == -1) {
335           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
336                         "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
337                         pf_dir_path, pf_dir_path2, strerror(errno));
338
339           g_free(pf_dir_path);
340         }
341         profile1->status = PROF_STAT_EXISTS;
342         g_free (profile1->reference);
343         profile1->reference = g_strdup(profile1->name);
344       }
345     }
346     fl1 = g_list_next(fl1);
347   }
348
349   /* Last remove deleted */
350   fl1 = g_list_first(current_profiles);
351   while (fl1) {
352     found = FALSE;
353     profile1 = (profile_def *) fl1->data;
354     fl2 = g_list_first(edited_profiles);
355     while (fl2) {
356       profile2 = (profile_def *) fl2->data;
357       if (strcmp(profile1->name, profile2->name)==0) {
358         /* Profile exists in both lists */
359         found = TRUE;
360       } else if (strcmp(profile1->name, profile2->reference)==0) {
361         /* Profile has been renamed */
362         found = TRUE;
363       }
364       fl2 = fl2->next;
365     }
366     if (!found) {
367       /* Exists in existing list and not in edited, this is a deleted profile */
368       if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
369         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
370                       "Can't delete profile directory\n\"%s\":\n%s.",
371                       pf_dir_path, strerror(errno));
372
373         g_free(pf_dir_path);
374       }
375     }
376     fl1 = g_list_next(fl1);
377   }
378
379   copy_profile_list();
380   profile_select(main_w, profile_l, destroy);
381 }
382
383 static void
384 profile_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
385 {
386   GtkWidget    *main_w = gtk_widget_get_toplevel(ok_bt);
387   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
388
389   /*
390    * Apply the profile and destroy the dialog box.
391    */
392   profile_apply(main_w, profile_l, TRUE);
393 }
394
395 static void
396 profile_dlg_apply_cb(GtkWidget *apply_bt, gpointer data _U_)
397 {
398   GtkWidget    *main_w    = gtk_widget_get_toplevel(apply_bt);
399   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
400
401   /*
402    * Apply the profile, but don't destroy the dialog box.
403    */
404   profile_apply(main_w, profile_l, FALSE);
405 }
406
407 /* cancel button pressed, revert changes and exit dialog */
408 static void
409 profile_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data _U_)
410 {
411   GtkWidget  *main_w = gtk_widget_get_toplevel(cancel_bt);
412
413   empty_profile_list (TRUE);
414   window_destroy(GTK_WIDGET(main_w));
415 }
416
417 /* Treat this as a cancel, by calling "profile_dlg_cancel_cb()" */
418 static gboolean
419 profile_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
420                             gpointer data)
421 {
422   profile_dlg_cancel_cb(main_w, data);
423   return FALSE;
424 }
425
426 static void
427 profile_dlg_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
428 {
429   global_profile_w = NULL;
430 }
431
432
433 static gint
434 profile_button_press_cb(GtkWidget *list, GdkEventButton *event, gpointer data _U_)
435 {
436   if (event->type == GDK_2BUTTON_PRESS) {
437     GtkWidget *main_w = gtk_widget_get_toplevel(list);
438
439     profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
440   }
441
442   return FALSE;
443 }
444
445 static gint
446 profile_key_release_cb(GtkWidget *list, GdkEventKey *event, gpointer data _U_)
447 {
448   if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
449     GtkWidget    *main_w = gtk_widget_get_toplevel(list);
450
451     profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
452   }
453
454   return FALSE;
455 }
456
457 static void
458 profile_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
459 {
460   GtkWidget    *profile_l   = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
461   GtkWidget    *main_w      = gtk_widget_get_toplevel(profile_l);
462   GtkTreeModel *model;
463   GtkTreeIter   iter;
464   GtkWidget    *name_te     = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
465   GtkWidget    *copy_bt     = g_object_get_data(G_OBJECT(main_w), E_PROF_COPY_BT_KEY);
466   GtkWidget    *del_bt      = g_object_get_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY);
467   profile_def  *profile;
468   gchar        *name        = NULL;
469   GList        *fl_entry;
470   gint          sensitivity = FALSE;
471
472   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
473     gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
474     if (fl_entry) {
475       profile= (profile_def *) fl_entry->data;
476       name   = g_strdup(profile->name);
477       if (profile->status!=PROF_STAT_DEFAULT) {
478         sensitivity = TRUE;
479       }
480     }
481   }
482
483   /*
484    * Did you know that this function is called when the window is destroyed?
485    * Funny, that.
486    * This means that we have to:
487    *
488    *    attach to the top-level window data items containing pointers to
489    *    the widgets we affect here;
490    *
491    *    give each of those widgets their own destroy callbacks;
492    *
493    *    clear that pointer when the widget is destroyed;
494    *
495    *    don't do anything to the widget if the pointer we get back is
496    *    null;
497    *
498    * so that if we're called after any of the widgets we'd affect are
499    * destroyed, we know that we shouldn't do anything to those widgets.
500    */
501   if (name_te != NULL) {
502     gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
503     gtk_widget_set_sensitive(name_te, sensitivity);
504   }
505   if (copy_bt != NULL)
506     gtk_widget_set_sensitive(copy_bt, sensitivity);
507   if (del_bt != NULL)
508     gtk_widget_set_sensitive(del_bt, sensitivity);
509   if (name != NULL)
510     g_free(name);
511 }
512
513 static void
514 profile_new_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
515 {
516   GtkWidget    *main_w = gtk_widget_get_toplevel(w);
517   GtkWidget    *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
518   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
519   GtkListStore *store;
520   GtkTreeIter   iter;
521   GList        *fl_entry;
522   const gchar  *name = "New profile";
523
524   /* Add a new entry to the profile list. */
525   fl_entry = add_to_profile_list(name, "", PROF_STAT_NEW);
526
527   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
528   gtk_list_store_append(store, &iter);
529   gtk_list_store_set(store, &iter, 0, name, 1, fl_entry, -1);
530   /* Select the item. */
531   gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
532
533   gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
534   gtk_widget_grab_focus(name_te);
535 }
536
537 #if 0
538 static void
539 profile_copy_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
540 {
541   GtkWidget    *main_w = gtk_widget_get_toplevel(w);
542   GtkWidget    *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
543   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
544   GtkListStore *store;
545   GtkTreeIter   iter;
546   GList        *fl_entry;
547   const gchar  *name = gtk_entry_get_text(GTK_ENTRY(name_te));
548   gchar        *new_name;
549
550   new_name = g_strdup_printf ("%s (copy)", name);
551
552   /* Add a new entry to the profile list. */
553   fl_entry = add_to_profile_list(new_name, name, PROF_STAT_COPY);
554
555   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
556   gtk_list_store_append(store, &iter);
557   gtk_list_store_set(store, &iter, 0, new_name, 1, fl_entry, -1);
558   /* Select the item. */
559   gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
560
561   gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
562   gtk_widget_grab_focus(name_te);
563
564   g_free (new_name);
565 }
566 #endif
567
568 static void
569 profile_name_te_changed_cb(GtkWidget *w, gpointer data _U_)
570 {
571   GtkWidget   *main_w = gtk_widget_get_toplevel(w);
572   GtkWidget   *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
573   GtkWidget   *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
574   profile_def *profile;
575   GList       *fl_entry;
576   const gchar *name = "";
577
578   GtkTreeSelection  *sel;
579   GtkTreeModel      *model;
580   GtkTreeIter        iter;
581
582   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
583   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
584
585   /* if something was selected */
586   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
587     gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
588     if (fl_entry != NULL) {
589       profile = (profile_def *) fl_entry->data;
590
591       if (strlen(name) > 0 && profile) {
592         if (profile->status != PROF_STAT_DEFAULT) {
593           g_free(profile->name);
594           profile->name = g_strdup(name);
595           if (profile->status != PROF_STAT_NEW) {
596             profile->status = PROF_STAT_CHANGED;
597           }
598           gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, name, -1);
599         }
600       }
601     }
602   }
603 }
604
605 static void
606 profile_del_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
607 {
608   GtkWidget  *main_w = gtk_widget_get_toplevel(w);
609   GtkWidget  *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
610   GList      *fl_entry;
611
612   GtkTreeSelection  *sel;
613   GtkTreeModel      *model;
614   GtkTreeIter        iter;
615
616   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
617   /* If something was selected */
618   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
619     gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
620
621     if (fl_entry != NULL) {
622       remove_from_profile_list (fl_entry);
623       gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
624     }
625   }
626
627   if (gtk_tree_model_get_iter_first (model, &iter)) {
628     gtk_tree_selection_select_iter(sel, &iter);
629   }
630 }
631
632 static GtkWidget *
633 profile_dialog_new(void)
634 {
635   GtkWidget  *main_w,  /* main window */
636     *main_vb,          /* main container */
637     *bbox,             /* button container */
638     *ok_bt,            /* "OK" button */
639     *apply_bt,         /* "Apply" button */
640     *cancel_bt,        /* "Cancel" button */
641     *help_bt;          /* "Help" button */
642   GtkWidget  *profile_vb,        /* profile settings box */
643     *props_vb;
644   GtkWidget  *top_hb,
645     *list_bb,
646     *new_bt,
647 #if 0
648     *copy_bt,
649 #endif
650     *del_bt,
651     *profile_sc,
652     *profile_l,
653     *middle_hb,
654     *name_lb,
655     *name_te,
656     *profile_fr,
657     *edit_fr,
658     *props_fr;
659   GtkTooltips       *tooltips;
660   GtkListStore      *store;
661   GtkCellRenderer   *renderer;
662   GtkTreeViewColumn *column;
663   GtkTreeSelection  *sel;
664   GtkTreeIter       *l_select;
665
666   /* Get a pointer to a static variable holding the type of profile on
667      which we're working, so we can pass that pointer to callback
668      routines. */
669
670   tooltips = gtk_tooltips_new ();
671
672   main_w = dlg_conf_window_new("Wireshark: Configuration Profiles");
673   gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
674
675   main_vb = gtk_vbox_new(FALSE, 0);
676   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
677   gtk_container_add(GTK_CONTAINER(main_w), main_vb);
678   gtk_widget_show(main_vb);
679
680   /* Container for each row of widgets */
681   profile_vb = gtk_vbox_new(FALSE, 0);
682   gtk_container_set_border_width(GTK_CONTAINER(profile_vb), 0);
683   gtk_container_add(GTK_CONTAINER(main_vb), profile_vb);
684   gtk_widget_show(profile_vb);
685
686   /* Top row: Buttons and profile list */
687   top_hb = gtk_hbox_new(FALSE, 0);
688   gtk_container_add(GTK_CONTAINER(profile_vb), top_hb);
689   gtk_widget_show(top_hb);
690
691   edit_fr = gtk_frame_new("Edit");
692   gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
693   gtk_widget_show(edit_fr);
694
695   list_bb = gtk_vbox_new(TRUE, 0);
696   gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
697   gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
698   gtk_widget_show(list_bb);
699
700   new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
701   g_signal_connect(new_bt, "clicked", G_CALLBACK(profile_new_bt_clicked_cb), NULL);
702   gtk_widget_show(new_bt);
703   gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
704   gtk_tooltips_set_tip (tooltips, new_bt,
705                         "Create a new profile (with default properties)", NULL);
706
707 #if 0
708   copy_bt = gtk_button_new_from_stock(GTK_STOCK_COPY);
709   gtk_widget_set_sensitive(copy_bt, FALSE);
710   g_signal_connect(copy_bt, "clicked", G_CALLBACK(profile_copy_bt_clicked_cb), NULL);
711   g_object_set_data(G_OBJECT(main_w), E_PROF_COPY_BT_KEY, copy_bt);
712   gtk_widget_show(copy_bt);
713   gtk_box_pack_start (GTK_BOX (list_bb), copy_bt, FALSE, FALSE, 0);
714   gtk_tooltips_set_tip (tooltips, copy_bt,
715                         "Copy the selected profile", NULL);
716 #endif
717
718   del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
719   gtk_widget_set_sensitive(del_bt, FALSE);
720   g_signal_connect(del_bt, "clicked", G_CALLBACK(profile_del_bt_clicked_cb), NULL);
721   g_object_set_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY, del_bt);
722   gtk_widget_show(del_bt);
723   gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
724   gtk_tooltips_set_tip (tooltips, del_bt, "Delete the selected profile", NULL);
725
726   profile_fr = gtk_frame_new("Configuration Profiles");
727   gtk_box_pack_start(GTK_BOX(top_hb), profile_fr, TRUE, TRUE, 0);
728   gtk_widget_show(profile_fr);
729
730   profile_sc = scrolled_window_new(NULL, NULL);
731   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(profile_sc),
732                                       GTK_SHADOW_IN);
733
734   gtk_container_set_border_width  (GTK_CONTAINER (profile_sc), 5);
735   gtk_container_add(GTK_CONTAINER(profile_fr), profile_sc);
736   gtk_widget_show(profile_sc);
737
738   store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
739   profile_l = tree_view_new(GTK_TREE_MODEL(store));
740   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(profile_l), FALSE);
741   renderer = gtk_cell_renderer_text_new();
742   column = gtk_tree_view_column_new_with_attributes("", renderer, "text", 0, NULL);
743   gtk_tree_view_column_set_sort_column_id(column, 0);
744   gtk_tree_view_append_column(GTK_TREE_VIEW(profile_l), column);
745   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
746   gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
747   g_signal_connect(sel, "changed", G_CALLBACK(profile_sel_list_cb), profile_vb);
748   g_signal_connect(profile_l, "button_press_event", G_CALLBACK(profile_button_press_cb), NULL);
749   g_signal_connect(profile_l, "key_release_event", G_CALLBACK(profile_key_release_cb), NULL);
750   g_object_set_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY, profile_l);
751   gtk_container_add(GTK_CONTAINER(profile_sc), profile_l);
752   gtk_widget_show(profile_l);
753
754   /* fill in data */
755   l_select = fill_list(main_w);
756
757   g_object_unref(G_OBJECT(store));
758
759   props_fr = gtk_frame_new("Properties");
760   gtk_box_pack_start(GTK_BOX(profile_vb), props_fr, FALSE, FALSE, 0);
761   gtk_widget_show(props_fr);
762
763   props_vb = gtk_vbox_new(FALSE, 3);
764   gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
765   gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
766   gtk_widget_show(props_vb);
767
768   /* row: Profile name entry */
769   middle_hb = gtk_hbox_new(FALSE, 3);
770   gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
771   gtk_widget_show(middle_hb);
772
773   name_lb = gtk_label_new("Profile name:");
774   gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
775   gtk_widget_show(name_lb);
776
777   name_te = gtk_entry_new();
778   gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
779   g_object_set_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY, name_te);
780   g_signal_connect(name_te, "changed", G_CALLBACK(profile_name_te_changed_cb), NULL);
781 #ifdef _WIN32
782   gtk_tooltips_set_tip (tooltips, name_te, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n   \\ / : * ? \" < > |", NULL);
783 #else
784   gtk_tooltips_set_tip (tooltips, name_te, "A profile name cannot contain the '/' character", NULL);
785 #endif
786   gtk_widget_show(name_te);
787
788   /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
789   bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
790   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
791   gtk_widget_show(bbox);
792
793   ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
794   g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_dlg_ok_cb), NULL);
795   gtk_tooltips_set_tip (tooltips, ok_bt, "Apply the profiles and close this dialog", NULL);
796
797   /* Catch the "activate" signal on the profile name and profile
798      list entries, so that if the user types Return
799      there, we act as if the "OK" button had been selected, as
800      happens if Return is typed if some widget that *doesn't*
801      handle the Return key has the input focus. */
802   dlg_set_activate(name_te, ok_bt);
803
804   apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
805   g_signal_connect(apply_bt, "clicked", G_CALLBACK(profile_dlg_apply_cb), NULL);
806   gtk_tooltips_set_tip (tooltips, apply_bt, "Apply the profiles and keep this dialog open", NULL);
807
808   cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
809   gtk_tooltips_set_tip (tooltips, cancel_bt, "Cancel the changes", NULL);
810   g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_dlg_cancel_cb), NULL);
811   window_set_cancel_button(main_w, cancel_bt, NULL);
812
813   help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
814   g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CONFIG_PROFILES_DIALOG);
815   gtk_tooltips_set_tip (tooltips, help_bt, "Show topic specific help", NULL);
816
817   if(ok_bt) {
818     gtk_widget_grab_default(ok_bt);
819   }
820
821
822   /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
823   /* otherwise the updatings can get confused */
824   if (l_select) {
825     gtk_tree_selection_select_iter(sel, l_select);
826     g_free(l_select);
827   }
828
829   if (profile_l) {
830     gtk_widget_grab_focus(profile_l);
831   }
832
833   g_signal_connect(main_w, "delete_event", G_CALLBACK(profile_dlg_delete_event_cb), NULL);
834   g_signal_connect(main_w, "destroy", G_CALLBACK(profile_dlg_destroy_cb), NULL);
835
836   gtk_widget_show(main_w);
837
838   window_present(main_w);
839
840   return main_w;
841 }
842
843
844 static void 
845 select_profile_cb (GtkWidget *w _U_, gpointer data)
846 {
847   const gchar *current_profile = get_profile_name ();
848   gchar       *selected_profile = (gchar *) data;
849
850   if (strcmp (selected_profile, current_profile) != 0) {
851     change_configuration_profile (selected_profile);
852   }
853 }
854
855 gboolean
856 profile_show_popup_cb (GtkWidget *w _U_, GdkEvent *event)
857 {
858   GdkEventButton *bevent = (GdkEventButton *)event;
859   const gchar    *profile_name = get_profile_name ();
860   const gchar    *profiles_dir, *name;
861   ETH_DIR        *dir;             /* scanned directory */
862   ETH_DIRENT     *file;            /* current file */
863   GtkWidget      *menu;
864   GtkWidget      *menu_item;
865
866   menu = gtk_menu_new ();
867
868   /* Add a menu item for the Default profile */
869   menu_item = gtk_check_menu_item_new_with_label (DEFAULT_PROFILE);
870   if (strcmp (profile_name, DEFAULT_PROFILE)==0) {
871     /* Check current profile */
872     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
873   }
874   g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
875   g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (DEFAULT_PROFILE));
876   gtk_menu_append (menu, menu_item);
877   gtk_widget_show (menu_item);
878
879   profiles_dir = get_profiles_dir();
880   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
881     while ((file = ws_dir_read_name(dir)) != NULL) {
882       name = ws_dir_get_name(file);
883
884       if (profile_exists(name)) {
885         menu_item = gtk_check_menu_item_new_with_label (name);
886         if (strcmp (name, profile_name)==0) {
887           /* Check current profile */
888           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
889         }
890         g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
891         g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (name));
892         gtk_menu_append (menu, menu_item);
893         gtk_widget_show (menu_item);
894       }
895     }
896   }
897
898   gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
899                   bevent->button, bevent->time);
900     
901   return TRUE;
902 }
903
904 /* Create a profile dialog for editing display profiles; this is to be used
905    as a callback for menu items, toolbars, etc.. */
906 void
907 profile_dialog_cb(GtkWidget *w _U_)
908 {
909   /* Has a profiles dialog box already been opened */
910   if (global_profile_w != NULL) {
911     /* Yes.  Just reactivate it. */
912     reactivate_window(global_profile_w);
913   } else {
914     global_profile_w = profile_dialog_new ();
915   }
916 }
917