~ubuntu-branches/ubuntu/trusty/unity-control-center/trusty

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2014-01-08 16:29:18 UTC
  • Revision ID: package-import@ubuntu.com-20140108162918-g29dd08tr913y2qh
Tags: upstream-14.04.0
ImportĀ upstreamĀ versionĀ 14.04.0

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