Back out parts of 38112, as pointed out by Stig Bj\370rlykke:
[obnox/wireshark/wip.git] / gtk / profile_dlg.c
1 /* profile_dlg.c
2  * Dialog box for profiles editing
3  * Stig Bjorlykke <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 #if GTK_CHECK_VERSION(3,0,0)
35 # include <gdk/gdkkeysyms-compat.h>
36 #endif
37
38 #include <epan/filesystem.h>
39 #include <epan/prefs.h>
40
41 #include "../simple_dialog.h"
42 #include <wsutil/file_util.h>
43
44 #include "gtk/main.h"
45 #include "gtk/menus.h"
46 #include "gtk/profile_dlg.h"
47 #include "gtk/dlg_utils.h"
48 #include "gtk/gui_utils.h"
49 #include "gtk/gtkglobals.h"
50 #include "gtk/help_dlg.h"
51 #include "gtk/recent.h"
52
53 enum {
54   NAME_COLUMN,
55   GLOBAL_COLUMN,
56   DATA_COLUMN,
57   NUM_COLUMNS
58 };
59
60 #define E_PROF_PROFILE_L_KEY        "profile_profile_l"
61 #define E_PROF_DEL_BT_KEY           "profile_del_bt"
62 #define E_PROF_NAME_TE_KEY          "profile_name_te"
63
64 static GtkWidget *global_profile_w = NULL;
65 static GList *current_profiles = NULL;
66 static GList *edited_profiles = NULL;
67
68 #define PROF_STAT_DEFAULT  1
69 #define PROF_STAT_EXISTS   2
70 #define PROF_STAT_NEW      3
71 #define PROF_STAT_CHANGED  4
72 #define PROF_STAT_COPY     5
73
74 #define PROF_OPERATION_NEW  1
75 #define PROF_OPERATION_EDIT 2
76
77 typedef struct {
78   char *name;           /* profile name */
79   char *reference;      /* profile reference */
80   int   status;
81   gboolean is_global;
82   gboolean from_global;
83 } profile_def;
84
85 static GList *
86 add_profile_entry(GList *fl, const char *profilename, const char *reference, int status, 
87                   gboolean is_global, gboolean from_global)
88 {
89     profile_def *profile;
90
91     profile = (profile_def *) g_malloc(sizeof(profile_def));
92     profile->name = g_strdup(profilename);
93     profile->reference = g_strdup(reference);
94     profile->status = status;
95     profile->is_global = is_global;
96     profile->from_global = from_global;
97     return g_list_append(fl, profile);
98 }
99
100 static GList *
101 remove_profile_entry(GList *fl, GList *fl_entry)
102 {
103   profile_def *profile;
104
105   profile = (profile_def *) fl_entry->data;
106   g_free(profile->name);
107   g_free(profile->reference);
108   g_free(profile);
109   return g_list_remove_link(fl, fl_entry);
110 }
111
112 static const gchar *
113 get_profile_parent (const gchar *profilename)
114 {
115   GList *fl_entry = g_list_first(edited_profiles);
116   guint no_edited = g_list_length(edited_profiles);
117   profile_def *profile;
118   guint i;
119
120   if (fl_entry) {
121     /* We have edited profiles, find parent */
122     for (i = 0; i < no_edited; i++) {
123       while (fl_entry) {
124         profile = (profile_def *) fl_entry->data;
125         if (strcmp (profile->name, profilename) == 0) {
126           if ((profile->status == PROF_STAT_NEW) ||
127               (profile->reference == NULL)) {
128             /* Copy from a new profile */
129             return NULL;
130           } else {
131             /* Found a parent, use this */
132             profilename = profile->reference;
133           }
134         }
135         fl_entry = g_list_next(fl_entry);
136       }
137       fl_entry = g_list_first(edited_profiles);
138     }
139   }
140
141   return profilename;
142 }
143
144 static GList *
145 add_to_profile_list(const char *name, const char *expression, int status, 
146                     gboolean is_global, gboolean from_global)
147 {
148   edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
149                                       is_global, from_global);
150
151   return g_list_last(edited_profiles);
152 }
153
154 static void
155 remove_from_profile_list(GList *fl_entry)
156 {
157   edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
158 }
159
160 static void
161 empty_profile_list(gboolean edit_list)
162 {
163   GList **flpp;
164
165   if (edit_list) {
166     flpp = &edited_profiles;
167
168     while(*flpp) {
169       *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
170     }
171
172     g_assert(g_list_length(*flpp) == 0);
173   }
174
175   flpp = &current_profiles;
176
177   while(*flpp) {
178     *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
179   }
180
181   g_assert(g_list_length(*flpp) == 0);
182 }
183
184 static void
185 copy_profile_list(void)
186 {
187     GList      *flp_src;
188     profile_def *profile;
189
190     flp_src = edited_profiles;
191
192     /* throw away the "old" destination list - a NULL list is ok here */
193     empty_profile_list(FALSE);
194
195     /* copy the list entries */
196     while(flp_src) {
197         profile = (flp_src)->data;
198
199         current_profiles = add_profile_entry(current_profiles, profile->name,
200                                              profile->reference, profile->status, 
201                                              profile->is_global, profile->from_global);
202         flp_src = g_list_next(flp_src);
203     }
204 }
205
206
207 static GtkTreeIter *
208 fill_list(GtkWidget *main_w)
209 {
210   WS_DIR        *dir;             /* scanned directory */
211   WS_DIRENT     *file;            /* current file */
212   GList         *fl_entry;
213   profile_def   *profile;
214   GtkTreeView   *profile_l;
215   GtkListStore  *store;
216   GtkTreeIter    iter, *l_select = NULL;
217   const gchar   *profile_name = get_profile_name ();
218   const gchar   *profiles_dir, *name;
219   gchar         *filename;
220
221   profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
222   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
223
224   fl_entry = add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, FALSE, FALSE);
225   gtk_list_store_append(store, &iter);
226   gtk_list_store_set(store, &iter, NAME_COLUMN, DEFAULT_PROFILE, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
227   if (strcmp (profile_name, DEFAULT_PROFILE)==0) {
228     l_select = g_memdup(&iter, sizeof(iter));
229   }
230
231   /* fill in data */
232   profiles_dir = get_profiles_dir();
233   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
234     while ((file = ws_dir_read_name(dir)) != NULL) {
235       name = ws_dir_get_name(file);
236       filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
237
238       if (test_for_directory(filename) == EISDIR) {
239         fl_entry = add_to_profile_list(name, name, PROF_STAT_EXISTS, FALSE, FALSE);
240         profile = (profile_def *) fl_entry->data;
241         gtk_list_store_append(store, &iter);
242         gtk_list_store_set(store, &iter, NAME_COLUMN, profile->name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
243
244         if (profile->name) {
245           if (strcmp(profile_name, profile->name) == 0) {
246             /*
247              * XXX - We're assuming that we can just copy a GtkTreeIter
248              * and use it later without any crashes.  This may not be a
249              * valid assumption.
250              */
251             l_select = g_memdup(&iter, sizeof(iter));
252           }
253         }
254       }
255       g_free (filename);
256     }
257     ws_dir_close (dir);
258   }
259
260   profiles_dir = get_global_profiles_dir();
261   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
262     while ((file = ws_dir_read_name(dir)) != NULL) {
263       name = ws_dir_get_name(file);
264       filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
265
266       if (test_for_directory(filename) == EISDIR) {
267         fl_entry = add_to_profile_list(name, name, PROF_STAT_EXISTS, TRUE, TRUE);
268         profile = (profile_def *) fl_entry->data;
269         gtk_list_store_append(store, &iter);
270         gtk_list_store_set(store, &iter, NAME_COLUMN, profile->name, GLOBAL_COLUMN, TRUE, DATA_COLUMN, fl_entry, -1);
271       }
272       g_free (filename);
273     }
274     ws_dir_close (dir);
275   }
276
277   /* Make the current list and the edited list equal */
278   copy_profile_list ();
279
280   return l_select;
281 }
282
283 static gboolean
284 profile_is_invalid_name(const gchar *name)
285 {
286   gchar  *message = NULL;
287
288 #ifdef _WIN32
289   char *invalid_dir_char = "\\/:*?\"<>|";
290   gboolean invalid = FALSE;
291   int i;
292
293   for (i = 0; i < 9; i++) {
294     if (strchr(name, invalid_dir_char[i])) {
295       /* Invalid character in directory */
296       invalid = TRUE;
297     }
298   }
299   if (name[0] == '.' || name[strlen(name)-1] == '.') {
300     /* Profile name cannot start or end with period */
301     invalid = TRUE;
302   }
303   if (invalid) {
304     message = g_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
305                               "   \\ / : * ? \" &lt; &gt; |");
306   }
307 #else
308   if (strchr(name, '/')) {
309     /* Invalid character in directory */
310     message = g_strdup_printf("contain the '/' character.");
311   }
312 #endif
313
314   if (message) {
315     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "A profile name cannot %s\nProfiles unchanged.", message);
316     g_free(message);
317     return TRUE;
318   }
319
320   return FALSE;
321 }
322
323 static void
324 profile_select(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
325 {
326   GList            *fl_entry;
327   profile_def      *profile;
328   GtkTreeSelection *sel;
329   GtkTreeModel     *model;
330   GtkTreeIter       iter;
331
332   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
333
334   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
335     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
336     if (fl_entry) {
337       profile = (profile_def *) fl_entry->data;
338       if (profile_exists (profile->name, FALSE) || profile_exists (profile->name, TRUE)) {
339         /* The new profile exists, change */
340         change_configuration_profile (profile->name);
341       } else if (!profile_exists (get_profile_name(), FALSE)) {
342         /* The new profile does not exist, and the previous profile has
343            been deleted.  Change to the default profile */
344         change_configuration_profile (NULL);
345       }
346     }
347   }
348
349   if (destroy) {
350     /*
351      * Destroy the profile dialog box.
352      */
353     empty_profile_list (TRUE);
354     window_destroy(main_w);
355   }
356 }
357
358 static void
359 profile_apply(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
360 {
361   char        *pf_dir_path, *pf_dir_path2, *pf_filename;
362   GList       *fl1, *fl2;
363   profile_def *profile1, *profile2;
364   gboolean     found;
365
366   /* First validate all profile names */
367   fl1 = g_list_first(edited_profiles);
368   while (fl1) {
369     profile1 = (profile_def *) fl1->data;
370     g_strstrip(profile1->name);
371     if (profile_is_invalid_name(profile1->name)) {
372       return;
373     }
374     fl1 = g_list_next(fl1);
375   }
376
377   /* Then do all copy profiles */
378   fl1 = g_list_first(edited_profiles);
379   while (fl1) {
380     profile1 = (profile_def *) fl1->data;
381     g_strstrip(profile1->name);
382     if (profile1->status == PROF_STAT_COPY) {
383       if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
384         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
385                       "Can't create directory\n\"%s\":\n%s.",
386                       pf_dir_path, g_strerror(errno));
387         
388         g_free(pf_dir_path);
389       }
390       profile1->status = PROF_STAT_EXISTS;
391
392       if (profile1->reference) {
393         if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
394                                       &pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
395           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
396                         "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
397                         pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
398
399           g_free(pf_filename);
400           g_free(pf_dir_path);
401           g_free(pf_dir_path2);
402         }
403       }
404
405       g_free (profile1->reference);
406       profile1->reference = g_strdup(profile1->name);
407     }
408     fl1 = g_list_next(fl1);
409   }
410
411
412   /* Then create new and rename changed */
413   fl1 = g_list_first(edited_profiles);
414   while (fl1) {
415     profile1 = (profile_def *) fl1->data;
416     g_strstrip(profile1->name);
417     if (profile1->status == PROF_STAT_NEW) {
418       /* We do not create a directory for the default profile */
419       if (strcmp(profile1->name, DEFAULT_PROFILE)!=0) {
420         if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
421           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
422                         "Can't create directory\n\"%s\":\n%s.",
423                         pf_dir_path, g_strerror(errno));
424
425           g_free(pf_dir_path);
426         }
427         profile1->status = PROF_STAT_EXISTS;
428
429         g_free (profile1->reference);
430         profile1->reference = g_strdup(profile1->name);
431       }
432     } else if (profile1->status == PROF_STAT_CHANGED) {
433       if (strcmp(profile1->reference, profile1->name)!=0) {
434         /* Rename old profile directory to new */
435         if (rename_persconffile_profile(profile1->reference, profile1->name,
436                                         &pf_dir_path, &pf_dir_path2) == -1) {
437           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
438                         "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
439                         pf_dir_path, pf_dir_path2, g_strerror(errno));
440
441           g_free(pf_dir_path);
442           g_free(pf_dir_path2);
443         }
444         profile1->status = PROF_STAT_EXISTS;
445         g_free (profile1->reference);
446         profile1->reference = g_strdup(profile1->name);
447       }
448     }
449     fl1 = g_list_next(fl1);
450   }
451
452   /* Last remove deleted */
453   fl1 = g_list_first(current_profiles);
454   while (fl1) {
455     found = FALSE;
456     profile1 = (profile_def *) fl1->data;
457     fl2 = g_list_first(edited_profiles);
458     while (fl2) {
459       profile2 = (profile_def *) fl2->data;
460       if (!profile2->is_global) {
461         if (strcmp(profile1->name, profile2->name)==0) {
462           /* Profile exists in both lists */
463           found = TRUE;
464         } else if (strcmp(profile1->name, profile2->reference)==0) {
465           /* Profile has been renamed */
466           found = TRUE;
467         }
468       }
469       fl2 = fl2->next;
470     }
471     if (!found) {
472       /* Exists in existing list and not in edited, this is a deleted profile */
473       if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
474         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
475                       "Can't delete profile directory\n\"%s\":\n%s.",
476                       pf_dir_path, g_strerror(errno));
477
478         g_free(pf_dir_path);
479       }
480     }
481     fl1 = g_list_next(fl1);
482   }
483
484   copy_profile_list();
485   profile_select(main_w, profile_l, destroy);
486 }
487
488 static void
489 profile_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
490 {
491   GtkWidget    *main_w = gtk_widget_get_toplevel(ok_bt);
492   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
493
494   /*
495    * Apply the profile and destroy the dialog box.
496    */
497   profile_apply(main_w, profile_l, TRUE);
498 }
499
500 static void
501 profile_dlg_apply_cb(GtkWidget *apply_bt, gpointer data _U_)
502 {
503   GtkWidget    *main_w    = gtk_widget_get_toplevel(apply_bt);
504   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
505
506   /*
507    * Apply the profile, but don't destroy the dialog box.
508    */
509   profile_apply(main_w, profile_l, FALSE);
510 }
511
512 /* cancel button pressed, revert changes and exit dialog */
513 static void
514 profile_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data _U_)
515 {
516   GtkWidget  *main_w = gtk_widget_get_toplevel(cancel_bt);
517
518   empty_profile_list (TRUE);
519   window_destroy(GTK_WIDGET(main_w));
520 }
521
522 /* Treat this as a cancel, by calling "profile_dlg_cancel_cb()" */
523 static gboolean
524 profile_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
525                             gpointer data)
526 {
527   profile_dlg_cancel_cb(main_w, data);
528   return FALSE;
529 }
530
531 static void
532 profile_dlg_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
533 {
534   global_profile_w = NULL;
535 }
536
537
538 static gboolean
539 profile_button_press_cb(GtkWidget *list, GdkEventButton *event, gpointer data _U_)
540 {
541   if (event->type == GDK_2BUTTON_PRESS) {
542     GtkWidget *main_w = gtk_widget_get_toplevel(list);
543
544     profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
545   }
546
547   return FALSE;
548 }
549
550 static gboolean
551 profile_key_release_cb(GtkWidget *list, GdkEventKey *event, gpointer data _U_)
552 {
553   if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
554     GtkWidget    *main_w = gtk_widget_get_toplevel(list);
555
556     profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
557   }
558
559   return FALSE;
560 }
561
562 static void
563 profile_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
564 {
565   GtkWidget    *profile_l   = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
566   GtkWidget    *main_w      = gtk_widget_get_toplevel(profile_l);
567   GtkTreeModel *model;
568   GtkTreeIter   iter;
569   GtkWidget    *name_te     = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
570   GtkWidget    *del_bt      = g_object_get_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY);
571   profile_def  *profile;
572   gchar        *name        = NULL;
573   GList        *fl_entry;
574   gint          sensitivity = FALSE;
575
576   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
577     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
578     if (fl_entry) {
579       profile = (profile_def *) fl_entry->data;
580       name = g_strdup(profile->name);
581       if ((profile->status != PROF_STAT_DEFAULT) && !profile->is_global) {
582         sensitivity = TRUE;
583       }
584     }
585   }
586
587   /*
588    * Did you know that this function is called when the window is destroyed?
589    * Funny, that.
590    * This means that we have to:
591    *
592    *    attach to the top-level window data items containing pointers to
593    *    the widgets we affect here;
594    *
595    *    give each of those widgets their own destroy callbacks;
596    *
597    *    clear that pointer when the widget is destroyed;
598    *
599    *    don't do anything to the widget if the pointer we get back is
600    *    null;
601    *
602    * so that if we're called after any of the widgets we'd affect are
603    * destroyed, we know that we shouldn't do anything to those widgets.
604    */
605   if (name_te != NULL) {
606     gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
607     gtk_widget_set_sensitive(name_te, sensitivity);
608   }
609   if (del_bt != NULL)
610     gtk_widget_set_sensitive(del_bt, sensitivity);
611   g_free(name);
612 }
613
614 static void
615 profile_new_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
616 {
617   GtkWidget    *main_w = gtk_widget_get_toplevel(w);
618   GtkWidget    *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
619   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
620   GtkListStore *store;
621   GtkTreeIter   iter;
622   GList        *fl_entry;
623   const gchar  *name = "New profile";
624
625   /* Add a new entry to the profile list. */
626   fl_entry = add_to_profile_list(name, "", PROF_STAT_NEW, FALSE, FALSE);
627
628   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
629   gtk_list_store_append(store, &iter);
630   gtk_list_store_set(store, &iter, NAME_COLUMN, name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
631   /* Select the item. */
632   gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
633
634   gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
635   gtk_widget_grab_focus(name_te);
636 }
637
638 static void
639 profile_copy_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
640 {
641   GtkWidget    *main_w = gtk_widget_get_toplevel(w);
642   GtkWidget    *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
643   GtkTreeView  *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
644   GtkListStore *store;
645   GtkTreeIter   iter;
646   GList        *fl_entry;
647   const gchar  *name = gtk_entry_get_text(GTK_ENTRY(name_te));
648   const gchar  *parent = NULL;
649   gchar        *new_name;
650
651   GtkTreeSelection *sel;
652   GtkTreeModel     *model;
653   profile_def   *profile = NULL;
654
655   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
656   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
657     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
658     if (fl_entry) {
659       profile = (profile_def *) fl_entry->data;
660     }
661   }
662
663   if (profile && profile->is_global) {
664     parent = profile->name;
665   } else {
666     parent = get_profile_parent (name);
667   }
668
669   if (profile && profile->is_global && !profile_exists (parent, FALSE)) {
670     new_name = g_strdup (name);
671   } else {
672     new_name = g_strdup_printf ("%s (copy)", name);
673   }
674
675   /* Add a new entry to the profile list. */
676   fl_entry = add_to_profile_list(new_name, parent, PROF_STAT_COPY, FALSE, profile ? profile->from_global : FALSE);
677
678   store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
679   gtk_list_store_append(store, &iter);
680   gtk_list_store_set(store, &iter, NAME_COLUMN, new_name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
681   /* Select the item. */
682   gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
683
684   gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
685   gtk_widget_grab_focus(name_te);
686
687   g_free (new_name);
688 }
689
690 static void
691 profile_name_te_changed_cb(GtkWidget *w, gpointer data _U_)
692 {
693   GtkWidget   *main_w = gtk_widget_get_toplevel(w);
694   GtkWidget   *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
695   GtkWidget   *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
696   profile_def *profile;
697   GList       *fl_entry;
698   const gchar *name = "";
699
700   GtkTreeSelection  *sel;
701   GtkTreeModel      *model;
702   GtkTreeIter        iter;
703
704   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
705   name   = gtk_entry_get_text(GTK_ENTRY(name_te));
706
707   /* if something was selected */
708   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
709     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
710     if (fl_entry != NULL) {
711       profile = (profile_def *) fl_entry->data;
712
713       if (strlen(name) > 0 && profile && !profile->is_global) {
714         if (profile->status != PROF_STAT_DEFAULT) {
715           g_free(profile->name);
716           profile->name = g_strdup(name);
717           if ((profile->status != PROF_STAT_NEW) &&
718               (profile->status != PROF_STAT_COPY)) {
719             profile->status = PROF_STAT_CHANGED;
720           }
721           gtk_list_store_set(GTK_LIST_STORE(model), &iter, NAME_COLUMN, name, -1);
722         }
723       }
724     }
725   }
726 }
727
728 static void
729 profile_del_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
730 {
731   GtkWidget  *main_w = gtk_widget_get_toplevel(w);
732   GtkWidget  *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
733   GList      *fl_entry;
734
735   GtkTreeSelection  *sel;
736   GtkTreeModel      *model;
737   GtkTreeIter        iter;
738
739   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
740   /* If something was selected */
741   if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
742     gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
743
744     if (fl_entry != NULL) {
745       remove_from_profile_list (fl_entry);
746       gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
747     }
748   }
749
750   if (gtk_tree_model_get_iter_first (model, &iter)) {
751     gtk_tree_selection_select_iter(sel, &iter);
752   }
753 }
754
755 static GtkWidget *
756 profile_dialog_new(void)
757 {
758   GtkWidget  *main_w,  /* main window */
759     *main_vb,          /* main container */
760     *bbox,             /* button container */
761     *ok_bt,            /* "OK" button */
762     *apply_bt,         /* "Apply" button */
763     *cancel_bt,        /* "Cancel" button */
764     *help_bt;          /* "Help" button */
765   GtkWidget  *profile_vb,        /* profile settings box */
766     *props_vb;
767   GtkWidget  *top_hb,
768     *list_bb,
769     *new_bt,
770     *copy_bt,
771     *del_bt,
772     *profile_sc,
773     *profile_l,
774     *middle_hb,
775     *name_lb,
776     *name_te,
777     *profile_fr,
778     *edit_fr,
779     *props_fr;
780   GtkListStore      *store;
781   GtkCellRenderer   *renderer;
782   GtkTreeViewColumn *column;
783   GtkTreeSelection  *sel;
784   GtkTreeIter       *l_select;
785   gboolean           has_global = has_global_profiles();
786   
787   /* Get a pointer to a static variable holding the type of profile on
788      which we're working, so we can pass that pointer to callback
789      routines. */
790
791   main_w = dlg_conf_window_new("Wireshark: Configuration Profiles");
792   gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
793
794   main_vb = gtk_vbox_new(FALSE, 0);
795   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
796   gtk_container_add(GTK_CONTAINER(main_w), main_vb);
797   gtk_widget_show(main_vb);
798
799   /* Container for each row of widgets */
800   profile_vb = gtk_vbox_new(FALSE, 0);
801   gtk_container_set_border_width(GTK_CONTAINER(profile_vb), 0);
802   gtk_container_add(GTK_CONTAINER(main_vb), profile_vb);
803   gtk_widget_show(profile_vb);
804
805   /* Top row: Buttons and profile list */
806   top_hb = gtk_hbox_new(FALSE, 0);
807   gtk_container_add(GTK_CONTAINER(profile_vb), top_hb);
808   gtk_widget_show(top_hb);
809
810   edit_fr = gtk_frame_new("Edit");
811   gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
812   gtk_widget_show(edit_fr);
813
814   list_bb = gtk_vbox_new(TRUE, 0);
815   gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
816   gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
817   gtk_widget_show(list_bb);
818
819   new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
820   g_signal_connect(new_bt, "clicked", G_CALLBACK(profile_new_bt_clicked_cb), NULL);
821   gtk_widget_show(new_bt);
822   gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
823   gtk_widget_set_tooltip_text (new_bt, "Create a new profile (with default properties)");
824
825   copy_bt = gtk_button_new_from_stock(GTK_STOCK_COPY);
826   g_signal_connect(copy_bt, "clicked", G_CALLBACK(profile_copy_bt_clicked_cb), NULL);
827   gtk_widget_show(copy_bt);
828   gtk_box_pack_start (GTK_BOX (list_bb), copy_bt, FALSE, FALSE, 0);
829   gtk_widget_set_tooltip_text (copy_bt, "Copy the selected profile");
830
831   del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
832   gtk_widget_set_sensitive(del_bt, FALSE);
833   g_signal_connect(del_bt, "clicked", G_CALLBACK(profile_del_bt_clicked_cb), NULL);
834   g_object_set_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY, del_bt);
835   gtk_widget_show(del_bt);
836   gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
837   gtk_widget_set_tooltip_text (del_bt, "Delete the selected profile");
838
839   profile_fr = gtk_frame_new("Configuration Profiles");
840   gtk_box_pack_start(GTK_BOX(top_hb), profile_fr, TRUE, TRUE, 0);
841   gtk_widget_show(profile_fr);
842
843   profile_sc = scrolled_window_new(NULL, NULL);
844   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(profile_sc),
845                                       GTK_SHADOW_IN);
846
847   gtk_container_set_border_width  (GTK_CONTAINER (profile_sc), 5);
848   gtk_container_add(GTK_CONTAINER(profile_fr), profile_sc);
849   gtk_widget_show(profile_sc);
850
851   store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
852   profile_l = tree_view_new(GTK_TREE_MODEL(store));
853   /* Only show headers if having more than one column */
854   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(profile_l), has_global);
855
856   renderer = gtk_cell_renderer_text_new();
857   column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", NAME_COLUMN, NULL);
858   gtk_tree_view_column_set_expand(column, TRUE);
859   gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN);
860   gtk_tree_view_append_column(GTK_TREE_VIEW(profile_l), column);
861
862   renderer = gtk_cell_renderer_toggle_new();
863   column = gtk_tree_view_column_new_with_attributes("Global", renderer, "active", GLOBAL_COLUMN, NULL);
864   gtk_tree_view_append_column(GTK_TREE_VIEW(profile_l), column);
865   gtk_widget_set_tooltip_text(column->button, "Global profiles will be copied to users profiles when used");
866   gtk_tree_view_column_set_visible(column, has_global);
867
868   sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
869   gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
870   g_signal_connect(sel, "changed", G_CALLBACK(profile_sel_list_cb), profile_vb);
871   g_signal_connect(profile_l, "button_press_event", G_CALLBACK(profile_button_press_cb), NULL);
872   g_signal_connect(profile_l, "key_release_event", G_CALLBACK(profile_key_release_cb), NULL);
873   g_object_set_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY, profile_l);
874   gtk_container_add(GTK_CONTAINER(profile_sc), profile_l);
875   gtk_widget_show(profile_l);
876
877   /* fill in data */
878   l_select = fill_list(main_w);
879
880   g_object_unref(G_OBJECT(store));
881
882   props_fr = gtk_frame_new("Properties");
883   gtk_box_pack_start(GTK_BOX(profile_vb), props_fr, FALSE, FALSE, 0);
884   gtk_widget_show(props_fr);
885
886   props_vb = gtk_vbox_new(FALSE, 3);
887   gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
888   gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
889   gtk_widget_show(props_vb);
890
891   /* row: Profile name entry */
892   middle_hb = gtk_hbox_new(FALSE, 3);
893   gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
894   gtk_widget_show(middle_hb);
895
896   name_lb = gtk_label_new("Profile name:");
897   gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
898   gtk_widget_show(name_lb);
899
900   name_te = gtk_entry_new();
901   gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
902   g_object_set_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY, name_te);
903   g_signal_connect(name_te, "changed", G_CALLBACK(profile_name_te_changed_cb), NULL);
904 #ifdef _WIN32
905   gtk_widget_set_tooltip_text (name_te, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n   \\ / : * ? \" < > |");
906 #else
907   gtk_widget_set_tooltip_text (name_te, "A profile name cannot contain the '/' character");
908 #endif
909   gtk_widget_show(name_te);
910
911   /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
912   bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
913   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
914   gtk_widget_show(bbox);
915
916   ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
917   g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_dlg_ok_cb), NULL);
918   gtk_widget_set_tooltip_text (ok_bt, "Apply the profiles and close this dialog");
919
920   /* Catch the "activate" signal on the profile name and profile
921      list entries, so that if the user types Return
922      there, we act as if the "OK" button had been selected, as
923      happens if Return is typed if some widget that *doesn't*
924      handle the Return key has the input focus. */
925   dlg_set_activate(name_te, ok_bt);
926
927   apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
928   g_signal_connect(apply_bt, "clicked", G_CALLBACK(profile_dlg_apply_cb), NULL);
929   gtk_widget_set_tooltip_text (apply_bt, "Apply the profiles and keep this dialog open");
930
931   cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
932   gtk_widget_set_tooltip_text (cancel_bt, "Cancel the changes");
933   g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_dlg_cancel_cb), NULL);
934   window_set_cancel_button(main_w, cancel_bt, NULL);
935
936   help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
937   g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CONFIG_PROFILES_DIALOG);
938   gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
939
940   if(ok_bt) {
941     gtk_widget_grab_default(ok_bt);
942   }
943
944
945   /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
946   /* otherwise the updatings can get confused */
947   if (l_select) {
948     gtk_tree_selection_select_iter(sel, l_select);
949     g_free(l_select);
950   }
951
952   if (profile_l) {
953     gtk_widget_grab_focus(profile_l);
954   }
955
956   g_signal_connect(main_w, "delete_event", G_CALLBACK(profile_dlg_delete_event_cb), NULL);
957   g_signal_connect(main_w, "destroy", G_CALLBACK(profile_dlg_destroy_cb), NULL);
958
959   gtk_widget_show(main_w);
960
961   window_present(main_w);
962
963   return main_w;
964 }
965
966
967 static void
968 select_profile_cb (GtkWidget *w _U_, gpointer data)
969 {
970   const gchar *current_profile = get_profile_name ();
971   gchar       *selected_profile = (gchar *) data;
972
973   if (strcmp (selected_profile, current_profile) != 0) {
974     change_configuration_profile (selected_profile);
975   }
976 }
977
978 gboolean
979 profile_show_popup_cb (GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_)
980 {
981   GdkEventButton *bevent = (GdkEventButton *)event;
982   const gchar    *profile_name = get_profile_name ();
983   const gchar    *profiles_dir, *name;
984   WS_DIR         *dir;             /* scanned directory */
985   WS_DIRENT      *file;            /* current file */
986   GtkWidget      *menu;
987   GtkWidget      *menu_item;
988
989   menu = gtk_menu_new ();
990
991   if (bevent->button != 1) {
992     GtkWidget *change_menu = menus_get_profiles_change_menu ();
993
994 #if GTK_CHECK_VERSION(2,16,0)
995     GtkWidget *edit_menu = menus_get_profiles_edit_menu ();
996     GtkWidget *delete_menu = menus_get_profiles_delete_menu ();
997     if (strcmp (profile_name, DEFAULT_PROFILE) != 0) {
998       gchar *label;
999       label = g_strdup_printf ("Edit \"%s\"...", profile_name);
1000       gtk_menu_item_set_label (GTK_MENU_ITEM(edit_menu), label);
1001       g_free (label);
1002       label = g_strdup_printf ("Delete \"%s\"", profile_name);
1003       gtk_menu_item_set_label (GTK_MENU_ITEM(delete_menu), label);
1004       g_free (label);
1005     } else {
1006       gtk_menu_item_set_label (GTK_MENU_ITEM(edit_menu), "Edit...");
1007       gtk_menu_item_set_label (GTK_MENU_ITEM(delete_menu), "Delete");
1008     }
1009 #endif
1010     gtk_menu_item_set_submenu (GTK_MENU_ITEM(change_menu), menu);
1011   }
1012
1013   /* Add a menu item for the Default profile */
1014   menu_item = gtk_check_menu_item_new_with_label (DEFAULT_PROFILE);
1015   if (strcmp (profile_name, DEFAULT_PROFILE) == 0) {
1016     /* Check current profile */
1017     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
1018   }
1019   g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
1020   g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (DEFAULT_PROFILE));
1021   gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
1022   gtk_widget_show (menu_item);
1023
1024   profiles_dir = get_profiles_dir();
1025   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
1026     while ((file = ws_dir_read_name(dir)) != NULL) {
1027       name = ws_dir_get_name(file);
1028
1029       if (profile_exists(name, FALSE)) {
1030         menu_item = gtk_check_menu_item_new_with_label (name);
1031         if (strcmp (name, profile_name)==0) {
1032           /* Check current profile */
1033           gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
1034         }
1035         g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
1036         g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (name));
1037         gtk_menu_shell_append  (GTK_MENU_SHELL (menu), menu_item);
1038         gtk_widget_show (menu_item);
1039       }
1040     }
1041     ws_dir_close (dir);
1042   }
1043
1044   profiles_dir = get_global_profiles_dir();
1045   if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
1046     GtkWidget *sub_menu = NULL;
1047     gboolean   added_submenu = FALSE;
1048   
1049     while ((file = ws_dir_read_name(dir)) != NULL) {
1050       name = ws_dir_get_name(file);
1051
1052       if (profile_exists(name, TRUE)) {
1053         if (!added_submenu) {
1054           menu_item =  gtk_separator_menu_item_new ();
1055           gtk_menu_shell_append  (GTK_MENU_SHELL (menu), menu_item);
1056           gtk_widget_show (menu_item);
1057           
1058           menu_item = gtk_menu_item_new_with_label ("New from Global");
1059           gtk_menu_shell_append  (GTK_MENU_SHELL (menu), menu_item);
1060           gtk_widget_show (menu_item);
1061
1062           sub_menu = gtk_menu_new ();
1063           gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_item), sub_menu);
1064
1065           added_submenu = TRUE;
1066         }
1067
1068         menu_item = gtk_menu_item_new_with_label (name);
1069         g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (name));
1070         if (profile_exists(name, FALSE)) {
1071           gtk_widget_set_sensitive(menu_item, FALSE);
1072         }
1073         gtk_menu_shell_append  (GTK_MENU_SHELL (sub_menu), menu_item);
1074         gtk_widget_show (menu_item);
1075       }
1076     }
1077     ws_dir_close (dir);
1078   }
1079
1080   if (bevent->button != 1) {
1081     /* Second-click is handled in popup_menu_handler() */
1082     return FALSE;
1083   }
1084
1085   gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
1086                   bevent->button, bevent->time);
1087
1088   return TRUE;
1089 }
1090
1091 static void
1092 profile_name_edit_ok (GtkWidget *w _U_, gpointer parent_w)
1093 {
1094   gint operation = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(w), "operation"));
1095   GtkComboBox  *combo_box = g_object_get_data (G_OBJECT(w), "create_from");
1096   GtkWidget    *entry = g_object_get_data (G_OBJECT(w), "entry");
1097   GtkTreeStore *store;
1098   GtkTreeIter iter;
1099   const gchar *new_name =  gtk_entry_get_text(GTK_ENTRY(entry));
1100   const gchar *profile_name = "";
1101   gboolean     from_global = FALSE;
1102   char        *pf_dir_path, *pf_dir_path2, *pf_filename;
1103
1104   if (strlen(new_name) == 0 || profile_is_invalid_name(new_name)) {
1105     return;
1106   }
1107
1108   switch (operation) {
1109   case PROF_OPERATION_NEW:
1110     if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
1111       store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
1112       gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &profile_name, 1, &from_global, -1);
1113     }
1114     break;
1115   case PROF_OPERATION_EDIT:
1116     profile_name = get_profile_name();
1117     if (strcmp(new_name, profile_name) == 0) {
1118       /* Rename without a change, do nothing */
1119       window_destroy(GTK_WIDGET(parent_w));
1120       return;
1121     }
1122     break;
1123   default:
1124     g_assert_not_reached();
1125   }
1126
1127   if (profile_exists (new_name, FALSE)) {
1128     simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
1129                   "The profile already exists:\n%s.", new_name);
1130     return;
1131   }
1132
1133   /* Write recent file for profile we are leaving */
1134   write_profile_recent();
1135
1136   switch (operation) {
1137   case PROF_OPERATION_NEW:
1138     if (create_persconffile_profile(new_name, &pf_dir_path) == -1) {
1139       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1140                     "Can't create directory\n\"%s\":\n%s.",
1141                     pf_dir_path, g_strerror(errno));
1142       
1143       g_free(pf_dir_path);
1144     } else if (strlen (profile_name) && 
1145                copy_persconffile_profile(new_name, profile_name, from_global, &pf_filename,
1146                                          &pf_dir_path, &pf_dir_path2) == -1)
1147     {
1148       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1149                     "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
1150                     pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
1151         
1152       g_free(pf_filename);
1153       g_free(pf_dir_path);
1154       g_free(pf_dir_path2);
1155     } else {
1156       change_configuration_profile (new_name);
1157     }
1158     break;
1159   case PROF_OPERATION_EDIT:
1160     if (rename_persconffile_profile(profile_name, new_name,
1161                                     &pf_dir_path, &pf_dir_path2) == -1) {
1162       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1163                     "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
1164                     pf_dir_path, pf_dir_path2, g_strerror(errno));
1165       
1166       g_free(pf_dir_path);
1167       g_free(pf_dir_path2);
1168     } else {
1169       change_configuration_profile (new_name);
1170     }
1171     break;
1172   default:
1173     g_assert_not_reached();
1174   }
1175
1176   window_destroy(GTK_WIDGET(parent_w));
1177 }
1178
1179 static void
1180 profile_name_edit_cancel (GtkWidget *w _U_, gpointer parent_w)
1181 {
1182   window_destroy(GTK_WIDGET(parent_w));
1183 }
1184
1185 static void
1186 profile_name_edit_dlg (gint operation)
1187 {
1188   WS_DIR      *dir;             /* scanned directory */
1189   WS_DIRENT   *file;            /* current file */
1190   GtkWidget   *win, *main_tb, *main_vb, *bbox, *cancel_bt, *ok_bt;
1191   GtkWidget   *entry, *label, *combo_box=NULL;
1192   GtkCellRenderer *cell;
1193   GtkTreeStore    *store;
1194   GtkTreeIter   iter, parent;
1195   gchar       *window_title=NULL;
1196   const gchar *profile_name, *profiles_dir, *name;
1197   gboolean     has_global = has_global_profiles();
1198
1199   profile_name = get_profile_name();
1200
1201   switch (operation) {
1202   case PROF_OPERATION_NEW:
1203     window_title = g_strdup ("Create New Profile");
1204     break;
1205   case PROF_OPERATION_EDIT:
1206     window_title = g_strdup_printf ("Edit: %s", profile_name);
1207     break;
1208   default:
1209     g_assert_not_reached();
1210   }
1211
1212   win = dlg_window_new(window_title);
1213   g_free (window_title);
1214
1215   gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
1216   gtk_window_resize(GTK_WINDOW(win), 400, 100);
1217
1218   main_vb = gtk_vbox_new(FALSE, 5);
1219   gtk_container_add(GTK_CONTAINER(win), main_vb);
1220   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
1221
1222   main_tb = gtk_table_new(2, 2, FALSE);
1223   gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
1224   gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
1225   gtk_table_set_row_spacings(GTK_TABLE(main_tb), 5);
1226
1227   if (operation == PROF_OPERATION_NEW) {
1228     label = gtk_label_new("Create from:");
1229     gtk_widget_set_tooltip_text (label, "All configuration files will be copied from this profile");
1230     gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
1231     gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
1232
1233     store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
1234     combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (store));
1235     gtk_widget_set_tooltip_text (combo_box, "All configuration files will be copied from this profile");
1236
1237     cell = gtk_cell_renderer_text_new();
1238     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell, TRUE);
1239     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell,
1240                                    "text", 0, "sensitive", 2,
1241                                    NULL);
1242
1243     gtk_tree_store_append(store, &iter, NULL);
1244     gtk_tree_store_set(store, &iter, 0, "", 1, FALSE, 2, TRUE, -1);
1245
1246     if (has_global) {
1247       gtk_tree_store_append(store, &parent, NULL);
1248       gtk_tree_store_set(store, &parent, 0, "Personal", 1, FALSE, 2, FALSE, -1);
1249     }
1250
1251     gtk_tree_store_append(store, &iter, has_global ? &parent : NULL);
1252     gtk_tree_store_set(store, &iter, 0, DEFAULT_PROFILE, 1, FALSE, 2, TRUE, -1);
1253     profiles_dir = get_profiles_dir();
1254     if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
1255       while ((file = ws_dir_read_name(dir)) != NULL) {
1256         name = ws_dir_get_name(file);
1257         if (profile_exists(name, FALSE)) {
1258           gtk_tree_store_append(store, &iter, has_global ? &parent : NULL);
1259           gtk_tree_store_set(store, &iter, 0, name, 1, FALSE, 2, TRUE, -1);
1260         }
1261       }
1262       ws_dir_close (dir);
1263     }
1264
1265     if (has_global) {
1266       gtk_tree_store_append(store, &parent, NULL);
1267       gtk_tree_store_set(store, &parent, 0, "Global", 1, FALSE, 2, FALSE, -1);
1268       profiles_dir = get_global_profiles_dir();
1269       if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
1270         while ((file = ws_dir_read_name(dir)) != NULL) {
1271           name = ws_dir_get_name(file);
1272           if (profile_exists(name, TRUE)) {
1273             gtk_tree_store_append(store, &iter, &parent);
1274             gtk_tree_store_set(store, &iter, 0, name, 1, TRUE, 2, TRUE, -1);
1275           }
1276         }
1277         ws_dir_close (dir);
1278       }
1279     }
1280     gtk_table_attach_defaults(GTK_TABLE(main_tb), combo_box, 1, 2, 0, 1);
1281     g_object_unref(store);
1282   }
1283
1284   label = gtk_label_new("Profile name:");
1285   gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 1, 2);
1286   gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
1287
1288   entry = gtk_entry_new();
1289   gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, 1, 2);
1290   switch (operation) {
1291   case PROF_OPERATION_NEW:
1292     gtk_entry_set_text(GTK_ENTRY(entry), "New profile");
1293     break;
1294   case PROF_OPERATION_EDIT:
1295     gtk_entry_set_text(GTK_ENTRY(entry), profile_name);
1296     break;
1297   default:
1298     g_assert_not_reached();
1299     break;
1300   }
1301 #ifdef _WIN32
1302   gtk_widget_set_tooltip_text (entry, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n   \\ / : * ? \" < > |");
1303 #else
1304   gtk_widget_set_tooltip_text (entry, "A profile name cannot contain the '/' character");
1305 #endif
1306
1307   bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
1308   gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
1309
1310   ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
1311   g_object_set_data (G_OBJECT(ok_bt), "entry", entry);
1312   g_object_set_data (G_OBJECT(ok_bt), "create_from", combo_box);
1313   g_object_set_data (G_OBJECT(ok_bt), "operation", GINT_TO_POINTER(operation));
1314   g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_name_edit_ok), win);
1315
1316   dlg_set_activate(entry, ok_bt);
1317   gtk_widget_grab_focus(entry);
1318
1319   cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
1320   g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_name_edit_cancel), win);
1321   window_set_cancel_button(win, cancel_bt, NULL);
1322
1323   gtk_widget_grab_default(ok_bt);
1324   gtk_widget_show_all(win);
1325 }
1326
1327 void
1328 profile_new_cb (GtkWidget *w _U_, gpointer data _U_)
1329 {
1330   profile_name_edit_dlg (PROF_OPERATION_NEW);
1331 }
1332
1333 void
1334 profile_delete_cb (GtkWidget *w _U_, gpointer data _U_)
1335 {
1336   const gchar *name = get_profile_name();
1337   char        *pf_dir_path;
1338
1339   if (profile_exists(name, FALSE) && strcmp (name, DEFAULT_PROFILE) != 0) {
1340     if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
1341       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
1342                     "Can't delete profile directory\n\"%s\":\n%s.",
1343                     pf_dir_path, g_strerror(errno));
1344       
1345       g_free(pf_dir_path);
1346     }
1347
1348     /* Change to the default profile */
1349     change_configuration_profile (NULL);
1350   }
1351 }
1352
1353 void
1354 profile_edit_cb (GtkWidget *w _U_, gpointer data _U_)
1355 {
1356   profile_name_edit_dlg (PROF_OPERATION_EDIT);
1357 }
1358
1359 /* Create a profile dialog for editing display profiles; this is to be used
1360    as a callback for menu items, toolbars, etc.. */
1361 void
1362 profile_dialog_cb(GtkWidget *w _U_)
1363 {
1364   /* Has a profiles dialog box already been opened */
1365   if (global_profile_w != NULL) {
1366     /* Yes.  Just reactivate it. */
1367     reactivate_window(global_profile_w);
1368   } else {
1369     global_profile_w = profile_dialog_new ();
1370   }
1371 }
1372