~ubuntu-branches/ubuntu/precise/gnome-control-center/precise-updates

« back to all changes in this revision

Viewing changes to panels/sound/gvc-sound-theme-chooser.c

Tags: upstream-3.0.1.1
ImportĀ upstreamĀ versionĀ 3.0.1.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2008 Bastien Nocera <hadess@hadess.net>
 
4
 * Copyright (C) 2008 William Jon McCann
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
19
 *
 
20
 */
 
21
 
 
22
#include "config.h"
 
23
 
 
24
#include <stdlib.h>
 
25
#include <stdio.h>
 
26
#include <unistd.h>
 
27
#include <utime.h>
 
28
#include <errno.h>
 
29
 
 
30
#include <glib.h>
 
31
#include <glib/gi18n-lib.h>
 
32
#include <gtk/gtk.h>
 
33
#include <canberra-gtk.h>
 
34
#include <libxml/tree.h>
 
35
 
 
36
#include <gconf/gconf-client.h>
 
37
 
 
38
#include "gvc-sound-theme-chooser.h"
 
39
#include "sound-theme-file-utils.h"
 
40
 
 
41
#define GVC_SOUND_THEME_CHOOSER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_SOUND_THEME_CHOOSER, GvcSoundThemeChooserPrivate))
 
42
 
 
43
struct GvcSoundThemeChooserPrivate
 
44
{
 
45
        GtkWidget *treeview;
 
46
        GtkWidget *selection_box;
 
47
        GConfClient *client;
 
48
        GSettings *sound_settings;
 
49
        guint metacity_dir_id;
 
50
        char *current_theme;
 
51
        char *current_parent;
 
52
};
 
53
 
 
54
static void     gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass);
 
55
static void     gvc_sound_theme_chooser_init       (GvcSoundThemeChooser      *sound_theme_chooser);
 
56
static void     gvc_sound_theme_chooser_finalize   (GObject            *object);
 
57
 
 
58
G_DEFINE_TYPE (GvcSoundThemeChooser, gvc_sound_theme_chooser, GTK_TYPE_VBOX)
 
59
 
 
60
#define KEY_SOUNDS_SCHEMA          "org.gnome.desktop.sound"
 
61
#define EVENT_SOUNDS_KEY           "event-sounds"
 
62
#define INPUT_SOUNDS_KEY           "input-feedback-sounds"
 
63
#define SOUND_THEME_KEY            "theme-name"
 
64
 
 
65
#define KEY_METACITY_DIR           "/apps/metacity/general"
 
66
#define AUDIO_BELL_KEY             KEY_METACITY_DIR "/audible_bell"
 
67
 
 
68
#define DEFAULT_ALERT_ID        "__default"
 
69
#define CUSTOM_THEME_NAME       "__custom"
 
70
#define NO_SOUNDS_THEME_NAME    "__no_sounds"
 
71
#define DEFAULT_THEME           "freedesktop"
 
72
 
 
73
enum {
 
74
        THEME_DISPLAY_COL,
 
75
        THEME_IDENTIFIER_COL,
 
76
        THEME_PARENT_ID_COL,
 
77
        THEME_NUM_COLS
 
78
};
 
79
 
 
80
enum {
 
81
        ALERT_DISPLAY_COL,
 
82
        ALERT_IDENTIFIER_COL,
 
83
        ALERT_SOUND_TYPE_COL,
 
84
        ALERT_NUM_COLS
 
85
};
 
86
 
 
87
enum {
 
88
        SOUND_TYPE_UNSET,
 
89
        SOUND_TYPE_OFF,
 
90
        SOUND_TYPE_DEFAULT_FROM_THEME,
 
91
        SOUND_TYPE_BUILTIN,
 
92
        SOUND_TYPE_CUSTOM
 
93
};
 
94
 
 
95
#define GVC_SOUND_SOUND    (xmlChar *) "sound"
 
96
#define GVC_SOUND_NAME     (xmlChar *) "name"
 
97
#define GVC_SOUND_FILENAME (xmlChar *) "filename"
 
98
 
 
99
/* Adapted from yelp-toc-pager.c */
 
100
static xmlChar *
 
101
xml_get_and_trim_names (xmlNodePtr node)
 
102
{
 
103
        xmlNodePtr cur;
 
104
        xmlChar *keep_lang = NULL;
 
105
        xmlChar *value;
 
106
        int j, keep_pri = INT_MAX;
 
107
 
 
108
        const gchar * const * langs = g_get_language_names ();
 
109
 
 
110
        value = NULL;
 
111
 
 
112
        for (cur = node->children; cur; cur = cur->next) {
 
113
                if (! xmlStrcmp (cur->name, GVC_SOUND_NAME)) {
 
114
                        xmlChar *cur_lang = NULL;
 
115
                        int cur_pri = INT_MAX;
 
116
 
 
117
                        cur_lang = xmlNodeGetLang (cur);
 
118
 
 
119
                        if (cur_lang) {
 
120
                                for (j = 0; langs[j]; j++) {
 
121
                                        if (g_str_equal (cur_lang, langs[j])) {
 
122
                                                cur_pri = j;
 
123
                                                break;
 
124
                                        }
 
125
                                }
 
126
                        } else {
 
127
                                cur_pri = INT_MAX - 1;
 
128
                        }
 
129
 
 
130
                        if (cur_pri <= keep_pri) {
 
131
                                if (keep_lang)
 
132
                                        xmlFree (keep_lang);
 
133
                                if (value)
 
134
                                        xmlFree (value);
 
135
 
 
136
                                value = xmlNodeGetContent (cur);
 
137
 
 
138
                                keep_lang = cur_lang;
 
139
                                keep_pri = cur_pri;
 
140
                        } else {
 
141
                                if (cur_lang)
 
142
                                        xmlFree (cur_lang);
 
143
                        }
 
144
                }
 
145
        }
 
146
 
 
147
        /* Delete all GVC_SOUND_NAME nodes */
 
148
        cur = node->children;
 
149
        while (cur) {
 
150
                xmlNodePtr this = cur;
 
151
                cur = cur->next;
 
152
                if (! xmlStrcmp (this->name, GVC_SOUND_NAME)) {
 
153
                        xmlUnlinkNode (this);
 
154
                        xmlFreeNode (this);
 
155
                }
 
156
        }
 
157
 
 
158
        return value;
 
159
}
 
160
 
 
161
static void
 
162
populate_model_from_node (GvcSoundThemeChooser *chooser,
 
163
                          GtkTreeModel         *model,
 
164
                          xmlNodePtr            node)
 
165
{
 
166
        xmlNodePtr child;
 
167
        xmlChar   *filename;
 
168
        xmlChar   *name;
 
169
 
 
170
        filename = NULL;
 
171
        name = xml_get_and_trim_names (node);
 
172
        for (child = node->children; child; child = child->next) {
 
173
                if (xmlNodeIsText (child)) {
 
174
                        continue;
 
175
                }
 
176
 
 
177
                if (xmlStrcmp (child->name, GVC_SOUND_FILENAME) == 0) {
 
178
                        filename = xmlNodeGetContent (child);
 
179
                } else if (xmlStrcmp (child->name, GVC_SOUND_NAME) == 0) {
 
180
                        /* EH? should have been trimmed */
 
181
                }
 
182
        }
 
183
 
 
184
        if (filename != NULL && name != NULL) {
 
185
                gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
 
186
                                                   NULL,
 
187
                                                   G_MAXINT,
 
188
                                                   ALERT_IDENTIFIER_COL, filename,
 
189
                                                   ALERT_DISPLAY_COL, name,
 
190
                                                   ALERT_SOUND_TYPE_COL, _("Built-in"),
 
191
                                                   -1);
 
192
        }
 
193
 
 
194
        xmlFree (filename);
 
195
        xmlFree (name);
 
196
}
 
197
 
 
198
static void
 
199
populate_model_from_file (GvcSoundThemeChooser *chooser,
 
200
                          GtkTreeModel         *model,
 
201
                          const char           *filename)
 
202
{
 
203
        xmlDocPtr  doc;
 
204
        xmlNodePtr root;
 
205
        xmlNodePtr child;
 
206
        gboolean   exists;
 
207
 
 
208
        exists = g_file_test (filename, G_FILE_TEST_EXISTS);
 
209
        if (! exists) {
 
210
                return;
 
211
        }
 
212
 
 
213
        doc = xmlParseFile (filename);
 
214
        if (doc == NULL) {
 
215
                return;
 
216
        }
 
217
 
 
218
        root = xmlDocGetRootElement (doc);
 
219
 
 
220
        for (child = root->children; child; child = child->next) {
 
221
                if (xmlNodeIsText (child)) {
 
222
                        continue;
 
223
                }
 
224
                if (xmlStrcmp (child->name, GVC_SOUND_SOUND) != 0) {
 
225
                        continue;
 
226
                }
 
227
 
 
228
                populate_model_from_node (chooser, model, child);
 
229
        }
 
230
 
 
231
        xmlFreeDoc (doc);
 
232
}
 
233
 
 
234
static void
 
235
populate_model_from_dir (GvcSoundThemeChooser *chooser,
 
236
                         GtkTreeModel         *model,
 
237
                         const char           *dirname)
 
238
{
 
239
        GDir       *d;
 
240
        const char *name;
 
241
 
 
242
        d = g_dir_open (dirname, 0, NULL);
 
243
        if (d == NULL) {
 
244
                return;
 
245
        }
 
246
 
 
247
        while ((name = g_dir_read_name (d)) != NULL) {
 
248
                char *path;
 
249
 
 
250
                if (! g_str_has_suffix (name, ".xml")) {
 
251
                        continue;
 
252
                }
 
253
 
 
254
                path = g_build_filename (dirname, name, NULL);
 
255
                populate_model_from_file (chooser, model, path);
 
256
                g_free (path);
 
257
        }
 
258
}
 
259
 
 
260
static gboolean
 
261
save_alert_sounds (GvcSoundThemeChooser  *chooser,
 
262
                   const char            *id)
 
263
{
 
264
        const char *sounds[3] = { "bell-terminal", "bell-window-system", NULL };
 
265
        char *path;
 
266
 
 
267
        if (strcmp (id, DEFAULT_ALERT_ID) == 0) {
 
268
                delete_old_files (sounds);
 
269
                delete_disabled_files (sounds);
 
270
        } else {
 
271
                delete_old_files (sounds);
 
272
                delete_disabled_files (sounds);
 
273
                add_custom_file (sounds, id);
 
274
        }
 
275
 
 
276
        /* And poke the directory so the theme gets updated */
 
277
        path = custom_theme_dir_path (NULL);
 
278
        if (utime (path, NULL) != 0) {
 
279
                g_warning ("Failed to update mtime for directory '%s': %s",
 
280
                           path, g_strerror (errno));
 
281
        }
 
282
        g_free (path);
 
283
 
 
284
        return FALSE;
 
285
}
 
286
 
 
287
 
 
288
static void
 
289
update_alert_model (GvcSoundThemeChooser  *chooser,
 
290
                    const char            *id)
 
291
{
 
292
        GtkTreeModel *model;
 
293
        GtkTreeIter   iter;
 
294
 
 
295
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
 
296
        gtk_tree_model_get_iter_first (model, &iter);
 
297
        do {
 
298
                char    *this_id;
 
299
 
 
300
                gtk_tree_model_get (model, &iter,
 
301
                                    ALERT_IDENTIFIER_COL, &this_id,
 
302
                                    -1);
 
303
 
 
304
                if (strcmp (this_id, id) == 0) {
 
305
                        GtkTreeSelection *selection;
 
306
 
 
307
                        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (chooser->priv->treeview));
 
308
                        gtk_tree_selection_select_iter (selection, &iter);
 
309
                }
 
310
 
 
311
                g_free (this_id);
 
312
        } while (gtk_tree_model_iter_next (model, &iter));
 
313
}
 
314
 
 
315
static void
 
316
save_theme_name (GvcSoundThemeChooser *chooser,
 
317
                 const char           *theme_name)
 
318
{
 
319
        /* If the name is empty, use "freedesktop" */
 
320
        if (theme_name == NULL || *theme_name == '\0') {
 
321
                theme_name = DEFAULT_THEME;
 
322
        }
 
323
 
 
324
        /* special case for no sounds */
 
325
        if (strcmp (theme_name, NO_SOUNDS_THEME_NAME) == 0) {
 
326
                g_settings_set_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY, FALSE);
 
327
                return;
 
328
        } else {
 
329
                g_settings_set_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY, TRUE);
 
330
        }
 
331
 
 
332
        g_settings_set_string (chooser->priv->sound_settings, SOUND_THEME_KEY, theme_name);
 
333
}
 
334
 
 
335
static gboolean
 
336
load_theme_file (const char *path,
 
337
                 char      **parent)
 
338
{
 
339
        GKeyFile *file;
 
340
        gboolean hidden;
 
341
 
 
342
        file = g_key_file_new ();
 
343
        if (g_key_file_load_from_file (file, path, G_KEY_FILE_KEEP_TRANSLATIONS, NULL) == FALSE) {
 
344
                g_key_file_free (file);
 
345
                return FALSE;
 
346
        }
 
347
        /* Don't add hidden themes to the list */
 
348
        hidden = g_key_file_get_boolean (file, "Sound Theme", "Hidden", NULL);
 
349
        if (!hidden) {
 
350
                /* Save the parent theme, if there's one */
 
351
                if (parent != NULL) {
 
352
                        *parent = g_key_file_get_string (file,
 
353
                                                         "Sound Theme",
 
354
                                                         "Inherits",
 
355
                                                         NULL);
 
356
                }
 
357
        }
 
358
 
 
359
        g_key_file_free (file);
 
360
 
 
361
        return TRUE;
 
362
}
 
363
 
 
364
static gboolean
 
365
load_theme_name (const char *name,
 
366
                 char      **parent)
 
367
{
 
368
        const char * const   *data_dirs;
 
369
        const char           *data_dir;
 
370
        char                 *path;
 
371
        guint                 i;
 
372
        gboolean              res;
 
373
 
 
374
        data_dir = g_get_user_data_dir ();
 
375
        path = g_build_filename (data_dir, "sounds", name, "index.theme", NULL);
 
376
        res = load_theme_file (path, parent);
 
377
        g_free (path);
 
378
        if (res)
 
379
                return TRUE;
 
380
 
 
381
        data_dirs = g_get_system_data_dirs ();
 
382
        for (i = 0; data_dirs[i] != NULL; i++) {
 
383
                path = g_build_filename (data_dirs[i], "sounds", name, "index.theme", NULL);
 
384
                res = load_theme_file (path, parent);
 
385
                g_free (path);
 
386
                if (res)
 
387
                        return TRUE;
 
388
        }
 
389
 
 
390
        return FALSE;
 
391
}
 
392
 
 
393
static void
 
394
update_alert (GvcSoundThemeChooser *chooser,
 
395
              const char           *alert_id)
 
396
{
 
397
        gboolean      is_custom;
 
398
        gboolean      is_default;
 
399
        gboolean      add_custom;
 
400
        gboolean      remove_custom;
 
401
 
 
402
        is_custom = strcmp (chooser->priv->current_theme, CUSTOM_THEME_NAME) == 0;
 
403
        is_default = strcmp (alert_id, DEFAULT_ALERT_ID) == 0;
 
404
 
 
405
        /* So a few possibilities:
 
406
         * 1. Named theme, default alert selected: noop
 
407
         * 2. Named theme, alternate alert selected: create new custom with sound
 
408
         * 3. Custom theme, default alert selected: remove sound and possibly custom
 
409
         * 4. Custom theme, alternate alert selected: update custom sound
 
410
         */
 
411
        add_custom = FALSE;
 
412
        remove_custom = FALSE;
 
413
        if (! is_custom && is_default) {
 
414
                /* remove custom just in case */
 
415
                remove_custom = TRUE;
 
416
        } else if (! is_custom && ! is_default) {
 
417
                if (chooser->priv->current_parent)
 
418
                        create_custom_theme (chooser->priv->current_parent);
 
419
                else
 
420
                        create_custom_theme (DEFAULT_THEME);
 
421
                save_alert_sounds (chooser, alert_id);
 
422
                add_custom = TRUE;
 
423
        } else if (is_custom && is_default) {
 
424
                save_alert_sounds (chooser, alert_id);
 
425
                /* after removing files check if it is empty */
 
426
                if (custom_theme_dir_is_empty ()) {
 
427
                        remove_custom = TRUE;
 
428
                }
 
429
        } else if (is_custom && ! is_default) {
 
430
                save_alert_sounds (chooser, alert_id);
 
431
        }
 
432
 
 
433
        if (add_custom) {
 
434
                save_theme_name (chooser, CUSTOM_THEME_NAME);
 
435
        } else if (remove_custom) {
 
436
                delete_custom_theme_dir ();
 
437
                if (is_custom) {
 
438
                        save_theme_name (chooser, chooser->priv->current_parent);
 
439
                }
 
440
        }
 
441
 
 
442
        update_alert_model (chooser, alert_id);
 
443
}
 
444
 
 
445
static void
 
446
play_preview_for_id (GvcSoundThemeChooser *chooser,
 
447
                     const char           *id)
 
448
{
 
449
        g_return_if_fail (id != NULL);
 
450
 
 
451
        /* special case: for the default item on custom themes
 
452
         * play the alert for the parent theme */
 
453
        if (strcmp (id, DEFAULT_ALERT_ID) == 0) {
 
454
                if (chooser->priv->current_parent != NULL) {
 
455
                        ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
 
456
                                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
 
457
                                                CA_PROP_EVENT_ID, "bell-window-system",
 
458
                                                CA_PROP_CANBERRA_XDG_THEME_NAME, chooser->priv->current_parent,
 
459
                                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
 
460
                                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
 
461
                                                CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
 
462
#ifdef CA_PROP_CANBERRA_ENABLE
 
463
                                                CA_PROP_CANBERRA_ENABLE, "1",
 
464
#endif
 
465
                                                NULL);
 
466
                } else {
 
467
                        ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
 
468
                                                CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
 
469
                                                CA_PROP_EVENT_ID, "bell-window-system",
 
470
                                                CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
 
471
                                                CA_PROP_CANBERRA_CACHE_CONTROL, "never",
 
472
                                                CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
 
473
#ifdef CA_PROP_CANBERRA_ENABLE
 
474
                                                CA_PROP_CANBERRA_ENABLE, "1",
 
475
#endif
 
476
                                                NULL);
 
477
                }
 
478
        } else {
 
479
                ca_gtk_play_for_widget (GTK_WIDGET (chooser), 0,
 
480
                                        CA_PROP_APPLICATION_NAME, _("Sound Preferences"),
 
481
                                        CA_PROP_MEDIA_FILENAME, id,
 
482
                                        CA_PROP_EVENT_DESCRIPTION, _("Testing event sound"),
 
483
                                        CA_PROP_CANBERRA_CACHE_CONTROL, "never",
 
484
                                        CA_PROP_APPLICATION_ID, "org.gnome.VolumeControl",
 
485
#ifdef CA_PROP_CANBERRA_ENABLE
 
486
                                        CA_PROP_CANBERRA_ENABLE, "1",
 
487
#endif
 
488
                                        NULL);
 
489
 
 
490
        }
 
491
}
 
492
 
 
493
static void
 
494
on_treeview_selection_changed (GtkTreeSelection     *selection,
 
495
                               GvcSoundThemeChooser *chooser)
 
496
{
 
497
        GtkTreeModel *model;
 
498
        GtkTreeIter   iter;
 
499
        char         *id;
 
500
 
 
501
        if (chooser->priv->treeview == NULL) {
 
502
                return;
 
503
        }
 
504
 
 
505
        model = gtk_tree_view_get_model (GTK_TREE_VIEW (chooser->priv->treeview));
 
506
 
 
507
        if (gtk_tree_selection_get_selected (selection, &model, &iter) == FALSE) {
 
508
                return;
 
509
        }
 
510
 
 
511
        id = NULL;
 
512
        gtk_tree_model_get (model, &iter,
 
513
                            ALERT_IDENTIFIER_COL, &id,
 
514
                            -1);
 
515
        if (id == NULL) {
 
516
                return;
 
517
        }
 
518
 
 
519
        play_preview_for_id (chooser, id);
 
520
        update_alert (chooser, id);
 
521
        g_free (id);
 
522
}
 
523
 
 
524
static gboolean
 
525
on_treeview_button_pressed (GtkTreeView          *treeview,
 
526
                            GdkEventButton       *event,
 
527
                            GvcSoundThemeChooser *chooser)
 
528
{
 
529
        GtkTreeSelection *selection;
 
530
        GtkTreePath      *path;
 
531
 
 
532
        selection = gtk_tree_view_get_selection (treeview);
 
533
        if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
 
534
                                           event->x, event->y, &path, NULL, NULL, NULL) == FALSE) {
 
535
                return FALSE;
 
536
        }
 
537
 
 
538
        if (gtk_tree_selection_path_is_selected (selection, path) == FALSE) {
 
539
                gtk_tree_path_free (path);
 
540
                return FALSE;
 
541
        }
 
542
        gtk_tree_path_free (path);
 
543
 
 
544
        on_treeview_selection_changed (selection, chooser);
 
545
 
 
546
        return FALSE;
 
547
}
 
548
 
 
549
static GtkWidget *
 
550
create_alert_treeview (GvcSoundThemeChooser *chooser)
 
551
{
 
552
        GtkListStore         *store;
 
553
        GtkWidget            *treeview;
 
554
        GtkCellRenderer      *renderer;
 
555
        GtkTreeViewColumn    *column;
 
556
        GtkTreeSelection     *selection;
 
557
 
 
558
        treeview = gtk_tree_view_new ();
 
559
        gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
 
560
        g_signal_connect (treeview,
 
561
                          "button-press-event",
 
562
                          G_CALLBACK (on_treeview_button_pressed),
 
563
                          chooser);
 
564
 
 
565
        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
 
566
        gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
 
567
        g_signal_connect (selection,
 
568
                          "changed",
 
569
                          G_CALLBACK (on_treeview_selection_changed),
 
570
                          chooser);
 
571
 
 
572
        /* Setup the tree model, 3 columns:
 
573
         * - display name
 
574
         * - sound id
 
575
         * - sound type
 
576
         */
 
577
        store = gtk_list_store_new (ALERT_NUM_COLS,
 
578
                                    G_TYPE_STRING,
 
579
                                    G_TYPE_STRING,
 
580
                                    G_TYPE_STRING);
 
581
 
 
582
        gtk_list_store_insert_with_values (store,
 
583
                                           NULL,
 
584
                                           G_MAXINT,
 
585
                                           ALERT_IDENTIFIER_COL, DEFAULT_ALERT_ID,
 
586
                                           ALERT_DISPLAY_COL, _("Default"),
 
587
                                           ALERT_SOUND_TYPE_COL, _("From theme"),
 
588
                                           -1);
 
589
 
 
590
        populate_model_from_dir (chooser, GTK_TREE_MODEL (store), SOUND_SET_DIR);
 
591
 
 
592
        gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
 
593
                                 GTK_TREE_MODEL (store));
 
594
 
 
595
        renderer = gtk_cell_renderer_text_new ();
 
596
        column = gtk_tree_view_column_new_with_attributes (_("Name"),
 
597
                                                           renderer,
 
598
                                                           "text", ALERT_DISPLAY_COL,
 
599
                                                           NULL);
 
600
        gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
 
601
 
 
602
        return treeview;
 
603
}
 
604
 
 
605
static int
 
606
get_file_type (const char *sound_name,
 
607
               char      **linked_name)
 
608
{
 
609
        char *name, *filename;
 
610
 
 
611
        *linked_name = NULL;
 
612
 
 
613
        name = g_strdup_printf ("%s.disabled", sound_name);
 
614
        filename = custom_theme_dir_path (name);
 
615
        g_free (name);
 
616
 
 
617
        if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) != FALSE) {
 
618
                g_free (filename);
 
619
                return SOUND_TYPE_OFF;
 
620
        }
 
621
        g_free (filename);
 
622
 
 
623
        /* We only check for .ogg files because those are the
 
624
         * only ones we create */
 
625
        name = g_strdup_printf ("%s.ogg", sound_name);
 
626
        filename = custom_theme_dir_path (name);
 
627
        g_free (name);
 
628
 
 
629
        if (g_file_test (filename, G_FILE_TEST_IS_SYMLINK) != FALSE) {
 
630
                *linked_name = g_file_read_link (filename, NULL);
 
631
                g_free (filename);
 
632
                return SOUND_TYPE_CUSTOM;
 
633
        }
 
634
        g_free (filename);
 
635
 
 
636
        return SOUND_TYPE_BUILTIN;
 
637
}
 
638
 
 
639
static void
 
640
update_alerts_from_theme_name (GvcSoundThemeChooser *chooser,
 
641
                               const char           *name)
 
642
{
 
643
        if (strcmp (name, CUSTOM_THEME_NAME) != 0) {
 
644
                /* reset alert to default */
 
645
                update_alert (chooser, DEFAULT_ALERT_ID);
 
646
        } else {
 
647
                int   sound_type;
 
648
                char *linkname;
 
649
 
 
650
                linkname = NULL;
 
651
                sound_type = get_file_type ("bell-terminal", &linkname);
 
652
                g_debug ("Found link: %s", linkname);
 
653
                if (sound_type == SOUND_TYPE_CUSTOM) {
 
654
                        update_alert (chooser, linkname);
 
655
                }
 
656
        }
 
657
}
 
658
 
 
659
static void
 
660
update_theme (GvcSoundThemeChooser *chooser)
 
661
{
 
662
        gboolean     events_enabled;
 
663
        char        *last_theme;
 
664
 
 
665
        events_enabled = g_settings_get_boolean (chooser->priv->sound_settings, EVENT_SOUNDS_KEY);
 
666
 
 
667
        last_theme = chooser->priv->current_theme;
 
668
        if (events_enabled) {
 
669
                chooser->priv->current_theme = g_settings_get_string (chooser->priv->sound_settings, SOUND_THEME_KEY);
 
670
        } else {
 
671
                chooser->priv->current_theme = g_strdup (NO_SOUNDS_THEME_NAME);
 
672
        }
 
673
 
 
674
        if (g_strcmp0 (last_theme, chooser->priv->current_theme) != 0) {
 
675
                g_free (chooser->priv->current_parent);
 
676
                if (load_theme_name (chooser->priv->current_theme,
 
677
                                     &chooser->priv->current_parent) == FALSE) {
 
678
                        g_free (chooser->priv->current_theme);
 
679
                        chooser->priv->current_theme = g_strdup (DEFAULT_THEME);
 
680
                        load_theme_name (DEFAULT_THEME,
 
681
                                         &chooser->priv->current_parent);
 
682
                }
 
683
        }
 
684
        g_free (last_theme);
 
685
 
 
686
        gtk_widget_set_sensitive (chooser->priv->selection_box, events_enabled);
 
687
 
 
688
        update_alerts_from_theme_name (chooser, chooser->priv->current_theme);
 
689
}
 
690
 
 
691
static GObject *
 
692
gvc_sound_theme_chooser_constructor (GType                  type,
 
693
                                     guint                  n_construct_properties,
 
694
                                     GObjectConstructParam *construct_params)
 
695
{
 
696
        GObject              *object;
 
697
        GvcSoundThemeChooser *self;
 
698
 
 
699
        object = G_OBJECT_CLASS (gvc_sound_theme_chooser_parent_class)->constructor (type, n_construct_properties, construct_params);
 
700
 
 
701
        self = GVC_SOUND_THEME_CHOOSER (object);
 
702
 
 
703
        update_theme (self);
 
704
 
 
705
        return object;
 
706
}
 
707
 
 
708
static void
 
709
gvc_sound_theme_chooser_class_init (GvcSoundThemeChooserClass *klass)
 
710
{
 
711
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
712
 
 
713
        object_class->constructor = gvc_sound_theme_chooser_constructor;
 
714
        object_class->finalize = gvc_sound_theme_chooser_finalize;
 
715
 
 
716
        g_type_class_add_private (klass, sizeof (GvcSoundThemeChooserPrivate));
 
717
}
 
718
 
 
719
static void
 
720
on_sound_settings_changed (GSettings            *settings,
 
721
                           const char           *key,
 
722
                           GvcSoundThemeChooser *chooser)
 
723
{
 
724
        if (strcmp (key, EVENT_SOUNDS_KEY) == 0) {
 
725
                update_theme (chooser);
 
726
        } else if (strcmp (key, SOUND_THEME_KEY) == 0) {
 
727
                update_theme (chooser);
 
728
        } else if (strcmp (key, INPUT_SOUNDS_KEY) == 0) {
 
729
                update_theme (chooser);
 
730
        }
 
731
}
 
732
 
 
733
static void
 
734
on_key_changed (GConfClient          *client,
 
735
                guint                 cnxn_id,
 
736
                GConfEntry           *entry,
 
737
                GvcSoundThemeChooser *chooser)
 
738
{
 
739
        const char *key;
 
740
 
 
741
        key = gconf_entry_get_key (entry);
 
742
 
 
743
        if (! g_str_has_prefix (key, KEY_METACITY_DIR)) {
 
744
                return;
 
745
        }
 
746
 
 
747
        if (strcmp (key, AUDIO_BELL_KEY) == 0) {
 
748
                update_theme (chooser);
 
749
        }
 
750
}
 
751
 
 
752
static void
 
753
setup_list_size_constraint (GtkWidget *widget,
 
754
                            GtkWidget *to_size)
 
755
{
 
756
        GtkRequisition req;
 
757
        int            max_height;
 
758
 
 
759
        /* constrain height to be the tree height up to a max */
 
760
        max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4;
 
761
 
 
762
        gtk_widget_get_preferred_size (to_size, NULL, &req);
 
763
 
 
764
        gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (widget),
 
765
                                                    MIN (req.height, max_height));
 
766
}
 
767
 
 
768
static void
 
769
gvc_sound_theme_chooser_init (GvcSoundThemeChooser *chooser)
 
770
{
 
771
        GtkWidget   *box;
 
772
        GtkWidget   *label;
 
773
        GtkWidget   *scrolled_window;
 
774
        GtkWidget   *alignment;
 
775
        char        *str;
 
776
 
 
777
        chooser->priv = GVC_SOUND_THEME_CHOOSER_GET_PRIVATE (chooser);
 
778
 
 
779
        chooser->priv->client = gconf_client_get_default ();
 
780
        chooser->priv->sound_settings = g_settings_new (KEY_SOUNDS_SCHEMA);
 
781
 
 
782
        str = g_strdup_printf ("<b>%s</b>", _("C_hoose an alert sound:"));
 
783
        chooser->priv->selection_box = box = gtk_frame_new (str);
 
784
        g_free (str);
 
785
        label = gtk_frame_get_label_widget (GTK_FRAME (box));
 
786
        gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
 
787
        gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
 
788
        gtk_frame_set_shadow_type (GTK_FRAME (box), GTK_SHADOW_NONE);
 
789
 
 
790
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
791
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
792
        gtk_container_add (GTK_CONTAINER (alignment), box);
 
793
        gtk_box_pack_start (GTK_BOX (chooser), alignment, TRUE, TRUE, 6);
 
794
 
 
795
        alignment = gtk_alignment_new (0, 0, 1, 1);
 
796
        gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
 
797
        gtk_container_add (GTK_CONTAINER (box), alignment);
 
798
 
 
799
        chooser->priv->treeview = create_alert_treeview (chooser);
 
800
        gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser->priv->treeview);
 
801
 
 
802
        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
 
803
        setup_list_size_constraint (scrolled_window, chooser->priv->treeview);
 
804
 
 
805
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
 
806
                                        GTK_POLICY_NEVER,
 
807
                                        GTK_POLICY_AUTOMATIC);
 
808
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
 
809
                                             GTK_SHADOW_IN);
 
810
        gtk_container_add (GTK_CONTAINER (scrolled_window), chooser->priv->treeview);
 
811
        gtk_container_add (GTK_CONTAINER (alignment), scrolled_window);
 
812
 
 
813
        g_signal_connect (G_OBJECT (chooser->priv->sound_settings), "changed",
 
814
                          G_CALLBACK (on_sound_settings_changed), chooser);
 
815
        gconf_client_add_dir (chooser->priv->client, KEY_METACITY_DIR,
 
816
                              GCONF_CLIENT_PRELOAD_ONELEVEL,
 
817
                              NULL);
 
818
        chooser->priv->metacity_dir_id = gconf_client_notify_add (chooser->priv->client,
 
819
                                                                  KEY_METACITY_DIR,
 
820
                                                                  (GConfClientNotifyFunc)on_key_changed,
 
821
                                                                  chooser, NULL, NULL);
 
822
}
 
823
 
 
824
static void
 
825
gvc_sound_theme_chooser_finalize (GObject *object)
 
826
{
 
827
        GvcSoundThemeChooser *sound_theme_chooser;
 
828
 
 
829
        g_return_if_fail (object != NULL);
 
830
        g_return_if_fail (GVC_IS_SOUND_THEME_CHOOSER (object));
 
831
 
 
832
        sound_theme_chooser = GVC_SOUND_THEME_CHOOSER (object);
 
833
 
 
834
        if (sound_theme_chooser->priv != NULL) {
 
835
                if (sound_theme_chooser->priv->metacity_dir_id > 0) {
 
836
                        gconf_client_notify_remove (sound_theme_chooser->priv->client,
 
837
                                                    sound_theme_chooser->priv->metacity_dir_id);
 
838
                        sound_theme_chooser->priv->metacity_dir_id = 0;
 
839
                }
 
840
                g_object_unref (sound_theme_chooser->priv->client);
 
841
                g_object_unref (sound_theme_chooser->priv->sound_settings);
 
842
                sound_theme_chooser->priv->client = NULL;
 
843
        }
 
844
 
 
845
        G_OBJECT_CLASS (gvc_sound_theme_chooser_parent_class)->finalize (object);
 
846
}
 
847
 
 
848
GtkWidget *
 
849
gvc_sound_theme_chooser_new (void)
 
850
{
 
851
        GObject *chooser;
 
852
        chooser = g_object_new (GVC_TYPE_SOUND_THEME_CHOOSER,
 
853
                                "spacing", 6,
 
854
                                NULL);
 
855
        return GTK_WIDGET (chooser);
 
856
}