source: trunk/third/evolution/shell/e-shell-folder-creation-dialog.c @ 18142

Revision 18142, 15.5 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18141, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* e-shell.c
3 *
4 * Copyright (C) 2000, 2001 Ximian, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Ettore Perazzoli
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <glib.h>
28#include <libgnome/gnome-defs.h>
29#include <libgnome/gnome-i18n.h>
30#include <libgnome/gnome-util.h>
31#include <glade/glade-xml.h>
32
33#include <gal/util/e-util.h>
34#include <gal/widgets/e-unicode.h>
35#include <gal/widgets/e-gui-utils.h>
36
37#include <gal/widgets/e-scroll-frame.h>
38
39#include "e-storage-set.h"
40#include "e-storage-set-view.h"
41#include "e-shell-utils.h"
42
43#include "e-shell-folder-creation-dialog.h"
44
45
46#define GLADE_FILE_NAME  EVOLUTION_GLADEDIR "/e-shell-folder-creation-dialog.glade"
47
48
49/* Data for the callbacks.  */
50struct _DialogData {
51        GtkWidget *dialog;
52        EShell *shell;
53
54        GtkWidget *folder_name_entry;
55        GtkWidget *storage_set_view;
56        GtkWidget *folder_type_option_menu;
57
58        GList *folder_types;
59
60        char *folder_path;
61
62        EShellFolderCreationDialogCallback result_callback;
63        void *result_callback_data;
64
65        gboolean creation_in_progress;
66};
67typedef struct _DialogData DialogData;
68
69static void
70dialog_data_destroy (DialogData *dialog_data)
71{
72        e_free_string_list (dialog_data->folder_types);
73        g_free (dialog_data->folder_path);
74
75        g_free (dialog_data);
76}
77
78
79/* Callback for the asynchronous folder creation function.  */
80
81static void
82async_create_cb (EStorageSet *storage_set,
83                 EStorageResult result,
84                 void *data)
85{
86        DialogData *dialog_data;
87
88        dialog_data = (DialogData *) data;
89
90        dialog_data->creation_in_progress = FALSE;
91
92        gnome_dialog_set_sensitive (GNOME_DIALOG (dialog_data->dialog), 0, TRUE);
93        gnome_dialog_set_sensitive (GNOME_DIALOG (dialog_data->dialog), 1, TRUE);
94
95        if (result == E_STORAGE_OK) {
96                /* Success! Tell the callback of this, then return */
97                if (dialog_data->result_callback != NULL)
98                        (* dialog_data->result_callback) (dialog_data->shell,
99                                                          E_SHELL_FOLDER_CREATION_DIALOG_RESULT_SUCCESS,
100                                                          dialog_data->folder_path,
101                                                          dialog_data->result_callback_data);
102                if (dialog_data->dialog != NULL) {
103                        gtk_widget_destroy (dialog_data->dialog);
104                } else {
105                        /* If dialog_data->dialog is NULL, it means that the
106                           dialog has been destroyed before we were done, so we
107                           have to free the dialog_data ourselves.  */
108                        dialog_data_destroy (dialog_data);
109                }
110                return;
111        }
112
113        /* Tell the callback something failed, then popup a dialog
114           explaining how it failed */
115        if (dialog_data->result_callback != NULL)
116                (* dialog_data->result_callback) (dialog_data->shell,
117                                                  E_SHELL_FOLDER_CREATION_DIALOG_RESULT_FAIL,
118                                                  dialog_data->folder_path,
119                                                  dialog_data->result_callback_data);
120
121        e_notice (GTK_WINDOW (dialog_data->dialog), GNOME_MESSAGE_BOX_ERROR,
122                  _("Cannot create the specified folder:\n%s"),
123                  e_storage_result_to_string (result));
124
125        /* If dialog_data->dialog is NULL, it means that the dialog has been
126           destroyed before we were done, so we have to free the dialog_data
127           ourselves.  */
128        if (dialog_data->dialog == NULL)
129                dialog_data_destroy (dialog_data);
130}
131
132
133/* Dialog signal callbacks.  */
134
135static void
136dialog_clicked_cb (GnomeDialog *dialog,
137                   int button_number,
138                   void *data)
139{
140        DialogData *dialog_data;
141        EStorageSet *storage_set;
142        GtkWidget *folder_type_menu_item;
143        const char *folder_type;
144        const char *parent_path;
145        const char *reason;
146        char *folder_name;
147        char *path;
148
149        dialog_data = (DialogData *) data;
150
151        if (button_number != 0) {
152                if (dialog_data->result_callback != NULL)
153                        (* dialog_data->result_callback) (dialog_data->shell,
154                                                          E_SHELL_FOLDER_CREATION_DIALOG_RESULT_CANCEL,
155                                                          NULL,
156                                                          dialog_data->result_callback_data);
157                gtk_widget_destroy (GTK_WIDGET (dialog));
158                return;
159        }
160
161        folder_name = e_utf8_gtk_entry_get_text (GTK_ENTRY (dialog_data->folder_name_entry));
162
163        if (! e_shell_folder_name_is_valid (folder_name, &reason)) {
164                e_notice (GTK_WINDOW (dialog), GNOME_MESSAGE_BOX_ERROR,
165                          _("The specified folder name is not valid: %s"), reason);
166                return;
167        }
168
169        parent_path = e_storage_set_view_get_current_folder
170                (E_STORAGE_SET_VIEW (dialog_data->storage_set_view));
171        if (parent_path == NULL) {
172                if (dialog_data->result_callback != NULL)
173                        (* dialog_data->result_callback) (dialog_data->shell,
174                                                          E_SHELL_FOLDER_CREATION_DIALOG_RESULT_CANCEL,
175                                                          NULL,
176                                                          dialog_data->result_callback_data);
177                gtk_widget_destroy (GTK_WIDGET (dialog));
178                return;
179        }
180
181        path = g_concat_dir_and_file (parent_path, folder_name);
182        g_free (folder_name);
183
184        storage_set = e_shell_get_storage_set (dialog_data->shell);
185
186        folder_type_menu_item = GTK_OPTION_MENU (dialog_data->folder_type_option_menu)->menu_item;
187        folder_type = gtk_object_get_data (GTK_OBJECT (folder_type_menu_item), "folder_type");
188
189        if (folder_type == NULL) {
190                g_warning ("Cannot get folder type for selected GtkOptionMenu item.");
191                return;
192        }
193
194        g_free (dialog_data->folder_path);
195        dialog_data->folder_path = path;
196
197        gnome_dialog_set_sensitive (dialog, 0, FALSE);
198        gnome_dialog_set_sensitive (dialog, 1, FALSE);
199
200        dialog_data->creation_in_progress = TRUE;
201
202        e_storage_set_async_create_folder (storage_set,
203                                           path,
204                                           folder_type,
205                                           "", /* description */
206                                           async_create_cb, dialog_data);
207}
208
209static void
210dialog_destroy_cb (GtkObject *object,
211                   void *data)
212{
213        DialogData *dialog_data;
214
215        dialog_data = (DialogData *) data;
216
217        if (dialog_data->creation_in_progress) {
218                /* If the dialog has been closed before we are done creating
219                   the folder, the dialog_data will be freed after the creation
220                   is completed.  */
221                dialog_data->dialog = NULL;
222                dialog_data->folder_name_entry = NULL;
223                dialog_data->storage_set_view = NULL;
224                dialog_data->folder_type_option_menu = NULL;
225                return;
226        }
227
228        dialog_data_destroy (dialog_data);
229}
230
231static void
232folder_name_entry_changed_cb (GtkEditable *editable,
233                              void *data)
234{
235        DialogData *dialog_data;
236        const char *parent_path;
237
238        dialog_data = (DialogData *) data;
239
240        parent_path = e_storage_set_view_get_current_folder (E_STORAGE_SET_VIEW (dialog_data->storage_set_view));
241
242        if (parent_path != NULL
243            && GTK_ENTRY (dialog_data->folder_name_entry)->text_length > 0)
244                gnome_dialog_set_sensitive (GNOME_DIALOG (dialog_data->dialog), 0, TRUE);
245        else
246                gnome_dialog_set_sensitive (GNOME_DIALOG (dialog_data->dialog), 0, FALSE);
247}
248
249static void
250storage_set_view_folder_selected_cb (EStorageSetView *storage_set_view,
251                                     const char *path,
252                                     void *data)
253{
254        DialogData *dialog_data;
255
256        dialog_data = (DialogData *) data;
257
258        if (GTK_ENTRY (dialog_data->folder_name_entry)->text_length > 0)
259                gnome_dialog_set_sensitive (GNOME_DIALOG (dialog_data->dialog), 0, TRUE);
260}
261
262
263/* Shell signal callbacks.  */
264
265static void
266shell_destroy_cb (GtkObject *object,
267                  void *data)
268{
269        GnomeDialog *dialog;
270
271        dialog = GNOME_DIALOG (data);
272        gtk_widget_destroy (GTK_WIDGET (dialog));
273}
274
275
276/* Dialog setup.  */
277
278static void
279setup_dialog (GtkWidget *dialog,
280              GladeXML *gui,
281              EShell *shell,
282              GtkWindow *parent_window)
283{
284        if (parent_window != NULL)
285                gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
286
287        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
288        gtk_window_set_title (GTK_WINDOW (dialog), _("Create New Folder"));
289
290        gnome_dialog_set_default   (GNOME_DIALOG (dialog), 0);
291        gnome_dialog_set_sensitive (GNOME_DIALOG (dialog), 0, FALSE);
292
293        gtk_widget_show (dialog);
294}
295
296static void
297setup_folder_name_entry (GtkWidget *dialog,
298                         GladeXML *gui,
299                         EShell *shell)
300{
301        GtkWidget *folder_name_entry;
302
303        folder_name_entry = glade_xml_get_widget (gui, "folder_name_entry");
304
305        gnome_dialog_editable_enters (GNOME_DIALOG (dialog), GTK_EDITABLE (folder_name_entry));
306}
307
308static GtkWidget *
309add_storage_set_view (GtkWidget *dialog,
310                      GladeXML *gui,
311                      EShell *shell,
312                      const char *default_parent_folder)
313{
314        EStorageSet *storage_set;
315        GtkWidget *storage_set_view;
316        GtkWidget *scroll_frame;
317        GtkWidget *vbox;
318
319        storage_set = e_shell_get_storage_set (shell);
320        storage_set_view = e_storage_set_create_new_view (storage_set, NULL);
321
322        e_storage_set_view_set_allow_dnd (E_STORAGE_SET_VIEW (storage_set_view), FALSE);
323
324        GTK_WIDGET_SET_FLAGS (storage_set_view, GTK_CAN_FOCUS);
325
326        if (default_parent_folder != NULL)
327                e_storage_set_view_set_current_folder (E_STORAGE_SET_VIEW (storage_set_view),
328                                                       default_parent_folder);
329
330        vbox = glade_xml_get_widget (gui, "main_vbox");
331
332        scroll_frame = e_scroll_frame_new (NULL, NULL);
333        e_scroll_frame_set_policy (E_SCROLL_FRAME (scroll_frame), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
334        e_scroll_frame_set_shadow_type (E_SCROLL_FRAME (scroll_frame), GTK_SHADOW_IN);
335        gtk_box_pack_start (GTK_BOX (vbox), scroll_frame, TRUE, TRUE, 0);
336
337        gtk_container_add (GTK_CONTAINER (scroll_frame), storage_set_view);
338
339        gtk_widget_show (scroll_frame);
340        gtk_widget_show (storage_set_view);
341
342        return storage_set_view;
343}
344
345struct _TypeWithDisplayName {
346        const char *type;
347        const char *display_name;
348};
349typedef struct _TypeWithDisplayName TypeWithDisplayName;
350
351static int
352type_with_display_name_compare_func (const void *a, const void *b)
353{
354        const TypeWithDisplayName *val_a, *val_b;
355
356        val_a = (const TypeWithDisplayName *) a;
357        val_b = (const TypeWithDisplayName *) b;
358
359        return g_strcasecmp (val_a->display_name, val_b->display_name);
360}
361
362static GList *
363add_folder_types (GtkWidget *dialog,
364                  GladeXML *gui,
365                  EShell *shell,
366                  const char *default_type)
367{
368        EFolderTypeRegistry *folder_type_registry;
369        GtkWidget *folder_type_option_menu;
370        GtkWidget *menu;
371        GList *folder_types;
372        GList *types_with_display_names;
373        GList *p;
374        int default_item;
375        int i;
376
377        folder_type_option_menu = glade_xml_get_widget (gui, "folder_type_option_menu");
378
379        /* KLUDGE.  So, GtkOptionMenu is badly broken.  It calculates its size
380           in `gtk_option_menu_set_menu()' instead of using `size_request()' as
381           any sane widget would do.  So, in order to avoid the "narrow
382           GtkOptionMenu" bug, we have to destroy the existing associated menu
383           and create a new one.  Life sucks.  */
384
385        menu = gtk_option_menu_get_menu (GTK_OPTION_MENU (folder_type_option_menu));
386        g_assert (menu != NULL);
387        gtk_widget_destroy (menu);
388
389        menu = gtk_menu_new ();
390
391        folder_type_registry = e_shell_get_folder_type_registry (shell);
392        g_assert (folder_type_registry != NULL);
393
394        folder_types = e_folder_type_registry_get_type_names (folder_type_registry);
395        if (folder_types == NULL)
396                return NULL;            /* Uh? */
397
398        types_with_display_names = NULL;
399        for (p = folder_types; p != NULL; p = p->next) {
400                TypeWithDisplayName *new;
401
402                new = g_new (TypeWithDisplayName, 1);
403                new->type = p->data;
404                new->display_name = e_folder_type_registry_get_display_name_for_type (folder_type_registry, new->type);
405
406                types_with_display_names = g_list_prepend (types_with_display_names, new);
407        }
408
409        types_with_display_names = g_list_sort (types_with_display_names, type_with_display_name_compare_func);
410
411        /* FIXME: Add icon (I don't feel like writing an alpha-capable thingie again).  */
412
413        default_item = 0;
414        i = 0;
415        for (p = types_with_display_names; p != NULL; p = p->next) {
416                const TypeWithDisplayName *type;
417                GtkWidget *menu_item;
418
419                type = (const TypeWithDisplayName *) p->data;
420
421                if (! e_folder_type_registry_type_is_user_creatable (folder_type_registry, type->type))
422                        continue;
423
424                menu_item = gtk_menu_item_new_with_label (type->display_name);
425                gtk_widget_show (menu_item);
426                gtk_menu_append (GTK_MENU (menu), menu_item);
427
428                gtk_object_set_data_full (GTK_OBJECT (menu_item), "folder_type", g_strdup (type->type), g_free);
429
430                if (strcmp (type->type, default_type ? default_type : "mail") == 0)
431                        default_item = i;
432
433                i ++;
434        }
435
436        for (p = types_with_display_names; p != NULL; p = p->next)
437                g_free (p->data);
438        g_list_free (types_with_display_names);
439
440        gtk_option_menu_set_menu (GTK_OPTION_MENU (folder_type_option_menu), menu);
441        gtk_widget_show (menu);
442
443        gtk_option_menu_set_history (GTK_OPTION_MENU (folder_type_option_menu), default_item);
444        gtk_widget_queue_resize (folder_type_option_menu);
445
446        return folder_types;
447}
448
449static const char *
450get_type_from_parent_path (EShell *shell,
451                           const char *path)
452{
453        EFolder *folder;
454        const char *folder_type;
455        EStorageSet *set;
456
457        set = e_shell_get_storage_set (shell);
458        folder = e_storage_set_get_folder (set, path);
459        if (folder == NULL) {
460                return "mail";
461        }
462
463        folder_type = e_folder_get_type_string (folder);
464        if (folder_type == NULL || strcmp (folder_type, "noselect") == 0) {
465                return "mail";
466        } else {
467                return folder_type;
468        }
469}
470
471
472/* FIXME: Currently this is modal.  I think it's OK, but if people think it is
473   not, we should change it to non-modal and make sure only one of these is
474   open at once.  Currently it relies on modality for this.  */
475void
476e_shell_show_folder_creation_dialog (EShell *shell,
477                                     GtkWindow *parent_window,
478                                     const char *default_parent_folder,
479                                     const char *default_type,
480                                     EShellFolderCreationDialogCallback result_callback,
481                                     void *result_callback_data)
482{
483        GladeXML *gui;
484        GtkWidget *dialog;
485        GtkWidget *storage_set_view;
486        GList *folder_types;
487        DialogData *dialog_data;
488
489        g_return_if_fail (shell != NULL);
490        g_return_if_fail (E_IS_SHELL (shell));
491
492        gui = glade_xml_new (GLADE_FILE_NAME, NULL);
493        if (gui == NULL) {
494                g_warning ("Cannot load Glade description file for the folder creation dialog -- %s",
495                           GLADE_FILE_NAME);
496                return;
497        }
498
499        dialog = glade_xml_get_widget (gui, "create_folder_dialog");
500
501        setup_dialog (dialog, gui, shell, parent_window);
502        setup_folder_name_entry (dialog, gui, shell);
503
504        storage_set_view = add_storage_set_view (dialog, gui, shell, default_parent_folder);
505        if (default_type == NULL) {
506                const char *dt;
507
508                dt = get_type_from_parent_path (shell, default_parent_folder);
509                folder_types = add_folder_types (dialog, gui, shell, dt);
510        } else {
511                folder_types = add_folder_types (dialog, gui, shell, default_type);
512        }
513
514        dialog_data = g_new (DialogData, 1);
515        dialog_data->dialog                  = dialog;
516        dialog_data->shell                   = shell;
517        dialog_data->folder_name_entry       = glade_xml_get_widget (gui, "folder_name_entry");
518        dialog_data->storage_set_view        = storage_set_view;
519        dialog_data->folder_type_option_menu = glade_xml_get_widget (gui, "folder_type_option_menu");
520        dialog_data->folder_types            = folder_types;
521        dialog_data->folder_path             = NULL;
522        dialog_data->result_callback         = result_callback;
523        dialog_data->result_callback_data    = result_callback_data;
524        dialog_data->creation_in_progress    = FALSE;
525
526        gtk_signal_connect (GTK_OBJECT (dialog), "clicked",
527                            GTK_SIGNAL_FUNC (dialog_clicked_cb), dialog_data);
528        gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
529                            GTK_SIGNAL_FUNC (dialog_destroy_cb), dialog_data);
530
531        gtk_signal_connect (GTK_OBJECT (dialog_data->folder_name_entry), "changed",
532                            GTK_SIGNAL_FUNC (folder_name_entry_changed_cb), dialog_data);
533
534        gtk_signal_connect (GTK_OBJECT (dialog_data->storage_set_view), "folder_selected",
535                            storage_set_view_folder_selected_cb, dialog_data);
536
537        gtk_signal_connect_while_alive (GTK_OBJECT (shell), "destroy",
538                                        GTK_SIGNAL_FUNC (shell_destroy_cb), dialog_data,
539                                        GTK_OBJECT (dialog));
540
541        gtk_object_unref (GTK_OBJECT (gui));
542}
Note: See TracBrowser for help on using the repository browser.