~ubuntu-branches/ubuntu/utopic/gxine/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/debian-changes/src/preferences.c

  • Committer: Package Import Robot
  • Author(s): Steve Langasek
  • Date: 2014-05-07 21:34:55 UTC
  • mfrom: (2.1.17 sid)
  • Revision ID: package-import@ubuntu.com-20140507213455-qnu5diwyyj8bkaap
Tags: 0.5.907-3ubuntu1
* Merge from Debian unstable, remaining changes:
  - debian/rules, debian/control: use dh-autoreconf at build time.
  - debian/control: Add Xb-Npp-xxx, Xb-Npp-Description and Xb-Npp-File
    fields.
  - src/script_engine.c: fix a remaining memory leak issue associated with
    using JS_EncodeString(), which somehow didn't get fixed upstream
  - debian/gxineplugin.links: Add a link to xulrunner-addons/plugins
    directory.
  - mime.default: Add dvd, vcd, svcd tags.
* Dropped changes, no longer needed:
  - debian/gxine.install: no need to diverge from Debian since we no longer
    ship a wrapper
* All other changes dropped, as they have been included upstream or in
  Debian.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2001-2007 the xine project
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify
5
 
 * it under the terms of the GNU General Public License as published by
6
 
 * the Free Software Foundation; either version 2 of the License, or
7
 
 * (at your option) any later version.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
 * GNU General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU General Public License
15
 
 * along with this program; if not, write to the Free Software
16
 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17
 
 *
18
 
 * Functions to stup and deal with a preferences dialog interfacing with
19
 
 * the xine config system.
20
 
 *
21
 
 * Richard Wareham <richwareham@users.sourceforge.net> -- March 2002
22
 
 * Darren Salt <dsalt@users.sourceforge.net> -- December 2004
23
 
 */
24
 
 
25
 
#include "globals.h"
26
 
 
27
 
#include <assert.h>
28
 
#include <X11/Xlib.h>
29
 
#include <gtk/gtk.h>
30
 
#include <gdk/gdk.h>
31
 
#include <gdk/gdkkeysyms.h>
32
 
#include <glib.h>
33
 
#include <stdio.h>
34
 
#include <stdlib.h>
35
 
#include <string.h>
36
 
#include <sys/types.h>
37
 
#include <sys/stat.h>
38
 
#include <unistd.h>
39
 
 
40
 
#include "mediamarks.h"
41
 
#include "preferences.h"
42
 
#include "ui.h"
43
 
#include "utils.h"
44
 
 
45
 
#include <jsapi.h>
46
 
 
47
 
#if defined(WITH_GUDEV) || defined(WITH_HAL)
48
 
# define GXINE_DEVICE_INFO
49
 
#endif
50
 
 
51
 
typedef struct {
52
 
  void *notebook; /* NULL - get from parent */
53
 
  GtkWidget *todefault, *revert, *editable, *label, *separator;
54
 
  int exp;
55
 
} pref_item_t;
56
 
#define PREF_ITEM_T(NODE) ((pref_item_t *)(NODE)->data)
57
 
 
58
 
typedef struct {
59
 
  GtkWidget *notebook; /* contains child pages, not this page */
60
 
  GtkWidget *page, *table;
61
 
  char *prefix, *label;
62
 
} pref_page_t;
63
 
#define PREF_PAGE_T(NODE) ((pref_page_t *)(NODE)->data)
64
 
 
65
 
typedef union {
66
 
  GtkWidget *notebook;
67
 
  pref_item_t item;
68
 
  pref_page_t page;
69
 
} pref_t;
70
 
#define PREF_T(NODE) ((pref_t *)(NODE)->data)
71
 
 
72
 
static GtkWidget  *prefs_dialog;
73
 
static GNode      *prefs = NULL;
74
 
static GData      *prefs_map = NULL;
75
 
static int         is_visible;
76
 
static GtkTooltips *tooltips = NULL;
77
 
static int         update_lock = 0;
78
 
 
79
 
 
80
 
static int select_show_prefs_internal (GNode *page, int exp)
81
 
{
82
 
  int state = 0;
83
 
  pref_t *item = page->data;
84
 
  pref_t *parent = page->parent ? page->parent->data : NULL;
85
 
  GNode *child;
86
 
 
87
 
  if (parent && parent->notebook == GINT_TO_POINTER (1))
88
 
    return 0; /* called too early */
89
 
 
90
 
  child = g_node_first_child (page);
91
 
 
92
 
  if (PREF_T(child)->notebook)
93
 
  {
94
 
    /* recursively process subpages (but first we hide them all) */
95
 
    GtkNotebook *notebook = GTK_NOTEBOOK(item->page.notebook);
96
 
    while (gtk_notebook_get_n_pages (notebook))
97
 
      gtk_notebook_remove_page (notebook, 0);
98
 
    for (; child; child = g_node_next_sibling (child))
99
 
      state |= select_show_prefs_internal (child, exp);
100
 
  }
101
 
  else
102
 
  {
103
 
    /* hide/show widgets on this page */
104
 
    pref_item_t *last = NULL;
105
 
    for (; child; child = g_node_next_sibling (child))
106
 
    {
107
 
      pref_item_t *pref = child->data;
108
 
      void (*func)(GtkWidget *);
109
 
      if (pref->exp <= exp)
110
 
      {
111
 
        func = gtk_widget_show;
112
 
        state |= 1;
113
 
        last = pref;
114
 
      }
115
 
      else
116
 
      {
117
 
        func = gtk_widget_hide;
118
 
        state |= 2;
119
 
      }
120
 
      if (pref->exp <= exp)
121
 
        last = pref;
122
 
      func (pref->todefault);
123
 
      func (pref->revert);
124
 
      func (pref->editable);
125
 
      func (pref->label);
126
 
      func (pref->separator);
127
 
    }
128
 
    if (last)
129
 
      gtk_widget_hide (last->separator);
130
 
  }
131
 
 
132
 
  /* re-add the page if it has visible widgets */
133
 
  if (parent && state != 2)
134
 
    gtk_notebook_append_page (GTK_NOTEBOOK(parent->notebook), item->page.page,
135
 
                              gtk_label_new (item->page.label));
136
 
 
137
 
  return state;
138
 
}
139
 
 
140
 
static gboolean select_show_pref_widgets (void *data)
141
 
{
142
 
  static const int experience_values[] = { 0, 10, 20, 30 };
143
 
  int exp = *(int *)data;
144
 
 
145
 
  if (exp >= 0 && exp < (int) G_N_ELEMENTS (experience_values))
146
 
    exp = experience_values[exp];
147
 
  else
148
 
    exp = 0;
149
 
 
150
 
  ++update_lock;
151
 
  gdk_threads_enter ();
152
 
  select_show_prefs_internal (prefs, exp);
153
 
  gdk_threads_leave ();
154
 
  --update_lock;
155
 
  return FALSE;
156
 
}
157
 
 
158
 
static gboolean entry_cb (GtkEntry *editable, GdkEventFocus *even,
159
 
                          gpointer user_data)
160
 
{
161
 
  xine_cfg_entry_t      entry;
162
 
  gchar                *key = (gchar *) user_data;
163
 
 
164
 
  if (update_lock)
165
 
    return FALSE;
166
 
 
167
 
  logprintf ("preferences: entry cb for key %s\n", key);
168
 
 
169
 
  if (!xine_config_lookup_entry (xine, key, &entry))
170
 
    return FALSE;
171
 
 
172
 
  key = gtk_editable_get_chars (GTK_EDITABLE(editable), 0, -1);
173
 
  if (!strcmp (entry.str_value, key))
174
 
    return FALSE;
175
 
 
176
 
  logprintf ("preferences: updating entry\n");
177
 
  entry.str_value = key;
178
 
  xine_config_update_entry (xine, &entry);
179
 
 
180
 
  return FALSE;
181
 
}
182
 
 
183
 
static gboolean entry_keypress_cb (GtkEntry *widget, GdkEventKey *event,
184
 
                                   gpointer data)
185
 
{
186
 
  switch (event->keyval)
187
 
  {
188
 
  case GDK_Return:
189
 
  case GDK_KP_Enter:
190
 
    entry_cb (widget, NULL, data);
191
 
    return TRUE;
192
 
 
193
 
  case GDK_minus:
194
 
    if ((event->state & GXINE_MODIFIER_MASK) != GDK_CONTROL_MASK)
195
 
      break;
196
 
  case GDK_Undo:
197
 
    {
198
 
      const char *key = g_object_get_data (G_OBJECT(widget), "cfg");
199
 
      xine_cfg_entry_t entry;
200
 
      if (key && xine_config_lookup_entry (xine, key, &entry))
201
 
        gtk_entry_set_text (widget, entry.str_value);
202
 
    }
203
 
    return TRUE;
204
 
  }
205
 
 
206
 
  return FALSE;
207
 
}
208
 
 
209
 
static void check_box_cb (GtkToggleButton *togglebutton, gpointer user_data)
210
 
{
211
 
  xine_cfg_entry_t      entry;
212
 
  gchar                *key = (gchar *) user_data;
213
 
  int                   state;
214
 
 
215
 
  if (update_lock)
216
 
    return;
217
 
 
218
 
  logprintf ("preferences: check box cb for key %s\n", key);
219
 
 
220
 
  if (!xine_config_lookup_entry (xine, key, &entry))
221
 
    return;
222
 
 
223
 
  state = gtk_toggle_button_get_active (togglebutton);
224
 
  if (entry.num_value == state)
225
 
    return;
226
 
 
227
 
  logprintf ("preferences: updating entry\n");
228
 
  entry.num_value = state;
229
 
  xine_config_update_entry (xine, &entry);
230
 
}
231
 
 
232
 
static void range_cb (GtkAdjustment *adj, const gchar *key)
233
 
{
234
 
  xine_cfg_entry_t      entry;
235
 
 
236
 
  if (update_lock)
237
 
    return;
238
 
 
239
 
  logprintf ("preferences: range cb for key %s\n", key);
240
 
 
241
 
  if (!xine_config_lookup_entry (xine, key, &entry) ||
242
 
      entry.num_value == adj->value)
243
 
    return;
244
 
 
245
 
  logprintf ("preferences: updating entry to %lf\n", adj->value);
246
 
  entry.num_value = adj->value;
247
 
  xine_config_update_entry (xine, &entry);
248
 
}
249
 
 
250
 
static void spin_cb (GtkSpinButton *widget, const gchar *key)
251
 
{
252
 
  xine_cfg_entry_t entry;
253
 
  int value;
254
 
 
255
 
  if (update_lock)
256
 
    return;
257
 
 
258
 
  logprintf ("preferences: spin cb for key %s\n", key);
259
 
 
260
 
  value = gtk_spin_button_get_value_as_int (widget);
261
 
  if (!xine_config_lookup_entry (xine, key, &entry) ||
262
 
      entry.num_value == value)
263
 
    return;
264
 
 
265
 
  logprintf ("preferences: updating entry to %d\n", value);
266
 
  entry.num_value = value;
267
 
  xine_config_update_entry (xine, &entry);
268
 
}
269
 
 
270
 
static void enum_cb (GtkWidget* widget, gpointer data)
271
 
{
272
 
  xine_cfg_entry_t      entry;
273
 
  gchar                *key = (gchar *) data;
274
 
  static int            pos;
275
 
 
276
 
  if (update_lock)
277
 
    return;
278
 
 
279
 
  logprintf ("preferences: enum cb for key %s\n", key);
280
 
 
281
 
  if (!xine_config_lookup_entry (xine, key, &entry))
282
 
    return;
283
 
 
284
 
  pos = gtk_combo_box_get_active (GTK_COMBO_BOX(widget));
285
 
 
286
 
  if (entry.num_value != pos)
287
 
  {
288
 
    entry.num_value = pos;
289
 
    logprintf ("preferences: updating entry to %d\n", pos);
290
 
    xine_config_update_entry (xine, &entry);
291
 
    if (!strcmp (key, "gui.experience_level"))
292
 
      g_idle_add ((GSourceFunc) select_show_pref_widgets, &pos);
293
 
  }
294
 
}
295
 
 
296
 
#ifdef WITH_GUDEV
297
 
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
298
 
#include <gudev/gudev.h>
299
 
 
300
 
static GUdevClient *gudev = NULL;
301
 
 
302
 
static gboolean check_gudev (void)
303
 
{
304
 
  static const gchar *const subsystems[] = { NULL }; /* at least DVB & block */
305
 
  if (!gudev)
306
 
    gudev = g_udev_client_new (subsystems);
307
 
  return !!gudev;
308
 
}
309
 
 
310
 
static char *
311
 
get_gudev_property (const char *name, const char *property, const char *sysfs)
312
 
{
313
 
  GUdevDevice *dev = g_udev_client_query_by_device_file (gudev, name);
314
 
  const gchar *prop;
315
 
  char *ret;
316
 
 
317
 
  if (!dev)
318
 
    return NULL;
319
 
 
320
 
  prop = property ? g_udev_device_get_property (dev, property) : NULL;
321
 
  if (prop && prop[0])
322
 
  {
323
 
    /* g_strcompress doesn't handle "\xNN" */
324
 
    char *ptr = ret = malloc (strlen (prop) + 1);
325
 
    char c;
326
 
    while ((c = *prop++))
327
 
    {
328
 
      if (c == '\\')
329
 
      {
330
 
        char tmp[] = { '0', prop[0], prop[1], prop[2], 0 };
331
 
        prop += 3;
332
 
        c = (char) strtol (tmp, NULL, 0);
333
 
      }
334
 
      *ptr++ = c;
335
 
    }
336
 
    *ptr = 0;
337
 
  }
338
 
  else
339
 
  {
340
 
    prop = sysfs ? g_udev_device_get_sysfs_attr (dev, sysfs) : NULL;
341
 
    ret = (prop && prop[0]) ? strdup (prop) : NULL;
342
 
  }
343
 
 
344
 
  g_object_unref ((GObject *)dev);
345
 
 
346
 
  return ret;
347
 
}
348
 
 
349
 
static char *
350
 
get_hal_device_info (const char *dev)
351
 
{
352
 
  if (!check_gudev () || !dev)
353
 
    return NULL;
354
 
 
355
 
  int ldev = strlen (dev);
356
 
  char buf[FILENAME_MAX + ldev + 1];
357
 
  strcpy (buf, dev); /* safe! */
358
 
  if (buf[0] == '/')
359
 
  {
360
 
    ldev = strrchr (buf, '/') - buf + 1;
361
 
    int tmp = readlink (dev, buf + ldev, FILENAME_MAX - 1);
362
 
    if (tmp <= 0)
363
 
      strcpy (buf, dev); /* safe! */
364
 
    else if (buf[ldev] == '/')
365
 
    {
366
 
      memmove (buf, buf + ldev, tmp);
367
 
      buf[tmp] = 0;
368
 
    }
369
 
    else
370
 
    {
371
 
      char *p;
372
 
      buf[ldev + tmp] = 0;
373
 
      while ((p = strstr (buf, "//")))
374
 
        memmove (p, p + 1, strlen (p));
375
 
      while ((p = strstr (buf, "/./")))
376
 
        memmove (p, p + 2, strlen (p) - 1);
377
 
      while ((p = strstr (buf, "/../")))
378
 
      {
379
 
        char *q = p;
380
 
        while (--q >= buf && *q != '/')
381
 
          /**/;
382
 
        memmove (q, p + 3, strlen (p) - 2);
383
 
      }
384
 
    }
385
 
  }
386
 
 
387
 
  return get_gudev_property (buf, "ID_MODEL_ENC", "name");
388
 
}
389
 
#elif defined(WITH_HAL)
390
 
#include <unistd.h>
391
 
#include <hal/libhal.h>
392
 
 
393
 
static LibHalContext *hal = NULL;
394
 
 
395
 
static gboolean check_hal (void)
396
 
{
397
 
  if (hal)
398
 
    return TRUE;
399
 
  DBusConnection *dbus = dbus_bus_get (DBUS_BUS_SYSTEM, NULL);
400
 
  if (!dbus)
401
 
    return FALSE;
402
 
  hal = libhal_ctx_new ();
403
 
  if (!hal)
404
 
    return FALSE;
405
 
  libhal_ctx_set_dbus_connection (hal, dbus);
406
 
  return TRUE;
407
 
}
408
 
 
409
 
static char *
410
 
get_hal_property (const char *dev, const char *dev_tag, const char *prop_tag)
411
 
{
412
 
  int count;
413
 
  char **devs = libhal_manager_find_device_string_match
414
 
                  (hal, dev_tag, dev, &count, NULL);
415
 
  if (!devs)
416
 
    return NULL;
417
 
  if (!devs[0])
418
 
  {
419
 
    libhal_free_string_array (devs);
420
 
    return NULL;
421
 
  }
422
 
 
423
 
  char *prop = libhal_device_get_property_string (hal, devs[0], prop_tag, NULL);
424
 
  char *ret = (prop && prop[0]) ? strdup (prop) : NULL;
425
 
  libhal_free_string (prop);
426
 
  libhal_free_string_array (devs);
427
 
  return ret;
428
 
}
429
 
 
430
 
static char *
431
 
get_hal_device_info (const char *dev)
432
 
{
433
 
  if (!check_hal () || !dev)
434
 
    return NULL;
435
 
 
436
 
  int ldev = strlen (dev);
437
 
  char buf[FILENAME_MAX + ldev + 1];
438
 
  strcpy (buf, dev); /* safe! */
439
 
  if (buf[0] == '/')
440
 
  {
441
 
    ldev = strrchr (buf, '/') - buf + 1;
442
 
    int tmp = readlink (dev, buf + ldev, FILENAME_MAX - 1);
443
 
    if (tmp <= 0)
444
 
      strcpy (buf, dev); /* safe! */
445
 
    else if (buf[ldev] == '/')
446
 
    {
447
 
      memmove (buf, buf + ldev, tmp);
448
 
      buf[tmp] = 0;
449
 
    }
450
 
    else
451
 
    {
452
 
      char *p;
453
 
      buf[ldev + tmp] = 0;
454
 
      while ((p = strstr (buf, "//")))
455
 
        memmove (p, p + 1, strlen (p));
456
 
      while ((p = strstr (buf, "/./")))
457
 
        memmove (p, p + 2, strlen (p) - 1);
458
 
      while ((p = strstr (buf, "/../")))
459
 
      {
460
 
        char *q = p;
461
 
        while (--q >= buf && *q != '/')
462
 
          /**/;
463
 
        memmove (q, p + 3, strlen (p) - 2);
464
 
      }
465
 
    }
466
 
  }
467
 
 
468
 
#ifdef HAL_DEVICE_FILE_PREFIX
469
 
  char *const buf_unprefixed =
470
 
    strncmp (buf, HAL_DEVICE_FILE_PREFIX, sizeof (HAL_DEVICE_FILE_PREFIX) - 1)
471
 
    ? "" : buf + sizeof (HAL_DEVICE_FILE_PREFIX) - 2;
472
 
#else
473
 
  char *const buf_unprefixed = buf;
474
 
#endif
475
 
 
476
 
  return get_hal_property (buf, "block.device", "storage.model")
477
 
         ? : get_hal_property (buf, "block.device", "info.product")
478
 
         ? : get_hal_property (buf_unprefixed, HAL_DEVICE_FILE_PROPERTY, "info.product")
479
 
         /* FIXME - BSD etc. */
480
 
         ;
481
 
}
482
 
#endif /* WITH_HAL */
483
 
 
484
 
#ifdef GXINE_DEVICE_INFO
485
 
static void
486
 
display_hal_device_info (GtkWidget *widget, const char *dev)
487
 
{
488
 
  GtkLabel *info = g_object_get_data (G_OBJECT (widget), "extra");
489
 
  if (info)
490
 
  {
491
 
    char *hal = get_hal_device_info (dev);
492
 
    char *content = g_markup_printf_escaped ("<small>%s</small>", hal ? : "");
493
 
    gtk_label_set_markup (info, content);
494
 
    free (content);
495
 
    free (hal);
496
 
  }
497
 
}
498
 
#endif /* WITH_GUDEV || WITH_HAL */
499
 
 
500
 
static void file_cb (GtkWidget *widget, int response, gpointer data)
501
 
{
502
 
  xine_cfg_entry_t entry;
503
 
  gchar *key = g_object_get_data (data, "cfg");
504
 
 
505
 
  if (update_lock || response != GTK_RESPONSE_ACCEPT)
506
 
    return;
507
 
 
508
 
  logprintf ("preferences: file cb for key %s\n", key);
509
 
 
510
 
  if (!xine_config_lookup_entry (xine, key, &entry))
511
 
    return;
512
 
 
513
 
  key = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (widget));
514
 
  if (key && strcmp (entry.str_value, key))
515
 
  {
516
 
    logprintf ("preferences: updating entry\n");
517
 
    entry.str_value = key;
518
 
    xine_config_update_entry (xine, &entry);
519
 
#ifdef GXINE_DEVICE_INFO
520
 
    if (entry.num_value == 2 /* XINE_CONFIG_STRING_IS_DEVICE_NAME */)
521
 
      display_hal_device_info (data, key);
522
 
#endif
523
 
  }
524
 
  free (key);
525
 
}
526
 
 
527
 
static void file_activate_cb (GtkWidget *widget, gpointer data)
528
 
{
529
 
  file_cb (widget, GTK_RESPONSE_ACCEPT, data);
530
 
}
531
 
 
532
 
#ifdef GXINE_DEVICE_INFO
533
 
static void file_preview_cb (GtkFileChooser *fc, gpointer data)
534
 
{
535
 
  GtkLabel *info = GTK_LABEL (gtk_file_chooser_get_preview_widget (fc));
536
 
  char *file = gtk_file_chooser_get_preview_filename (fc);
537
 
  if (!file)
538
 
    file = gtk_file_chooser_get_filename (fc);
539
 
  char *content = get_hal_device_info (file);
540
 
  gtk_label_set_text (info, content ? : "");
541
 
  free (content);
542
 
  g_free (file);
543
 
}
544
 
#endif
545
 
 
546
 
static void file_map_cb (GtkFileChooser *fcb, gpointer fc)
547
 
{
548
 
  if (!g_object_get_data (fc, "gxine"))
549
 
  {
550
 
    g_signal_connect_swapped (fcb, "current-folder-changed",
551
 
                              G_CALLBACK(file_activate_cb), fc);
552
 
    g_object_set_data (fc, "gxine", fc);
553
 
  }
554
 
#ifdef GXINE_DEVICE_INFO
555
 
  file_preview_cb (fcb, fc);
556
 
#endif
557
 
}
558
 
 
559
 
static void
560
 
default_item_cb (GtkWidget *w, gpointer key)
561
 
{
562
 
  xine_cfg_entry_t entry;
563
 
  xine_config_lookup_entry (xine, key, &entry);
564
 
  if (entry.type == XINE_CONFIG_TYPE_STRING)
565
 
    entry.str_value = entry.str_default;
566
 
  else
567
 
    entry.num_value = entry.num_default;
568
 
  preferences_update_entry (&entry);
569
 
}
570
 
 
571
 
static inline GtkWidget *
572
 
create_item_default (const xine_cfg_entry_t * entry)
573
 
{
574
 
  char *tip;
575
 
  gboolean alloc = FALSE;
576
 
 
577
 
  switch (entry->type)
578
 
  {
579
 
  case XINE_CONFIG_TYPE_ENUM:
580
 
    tip = entry->enum_values[entry->num_default];
581
 
    break;
582
 
 
583
 
  case XINE_CONFIG_TYPE_RANGE:
584
 
  case XINE_CONFIG_TYPE_NUM:
585
 
    alloc = TRUE;
586
 
    tip = g_strdup_printf ("%d", entry->num_default);
587
 
    break;
588
 
 
589
 
  case XINE_CONFIG_TYPE_BOOL:
590
 
    tip = entry->num_default ? "✓" : "✗";
591
 
    break;
592
 
 
593
 
  case XINE_CONFIG_TYPE_STRING:
594
 
    tip = entry->str_default;
595
 
    break;
596
 
 
597
 
  default:
598
 
    return NULL;
599
 
  }
600
 
 
601
 
  GtkWidget *widget = ui_button_new_stock (GTK_STOCK_CLEAR);
602
 
  gtk_tooltips_set_tip (tooltips, widget, tip, NULL);
603
 
  if (alloc)
604
 
    free (tip);
605
 
 
606
 
  g_signal_connect ((GObject *) widget, "clicked",
607
 
                    G_CALLBACK (default_item_cb), (gpointer) entry->key);
608
 
  return widget;
609
 
}
610
 
 
611
 
static void
612
 
revert_item_cb (GtkWidget *w, gpointer key)
613
 
{
614
 
  xine_cfg_entry_t entry;
615
 
  xine_config_lookup_entry (xine, key, &entry);
616
 
  if (entry.type == XINE_CONFIG_TYPE_STRING)
617
 
    entry.str_value = g_object_get_data ((GObject *) w, "revert");
618
 
  else
619
 
    entry.num_value = (int) (intptr_t) g_object_get_data ((GObject *) w, "revert");
620
 
  preferences_update_entry (&entry);
621
 
}
622
 
 
623
 
static inline GtkWidget *
624
 
create_item_revert (const xine_cfg_entry_t *entry)
625
 
{
626
 
  intptr_t value;
627
 
  char *tip;
628
 
  gboolean alloc = FALSE;
629
 
 
630
 
  switch (entry->type)
631
 
  {
632
 
  case XINE_CONFIG_TYPE_ENUM:
633
 
    tip = entry->enum_values[entry->num_value];
634
 
    value = entry->num_value;
635
 
    break;
636
 
 
637
 
  case XINE_CONFIG_TYPE_RANGE:
638
 
  case XINE_CONFIG_TYPE_NUM:
639
 
    alloc = TRUE;
640
 
    tip = g_strdup_printf ("%d", entry->num_value);
641
 
    value = entry->num_value;
642
 
    break;
643
 
 
644
 
  case XINE_CONFIG_TYPE_BOOL:
645
 
    tip = entry->num_value ? "✓" : "✗";
646
 
    value = entry->num_value;
647
 
    break;
648
 
 
649
 
  case XINE_CONFIG_TYPE_STRING:
650
 
    tip = entry->str_value;
651
 
    value = (intptr_t) strdup (entry->str_value);
652
 
    break;
653
 
 
654
 
  default:
655
 
    return NULL;
656
 
  }
657
 
 
658
 
  GtkWidget *widget = ui_button_new_stock (GTK_STOCK_REVERT_TO_SAVED);
659
 
  g_object_set_data ((GObject *) widget, "revert", (void *) value);
660
 
  g_signal_connect ((GObject *) widget, "clicked",
661
 
                    G_CALLBACK (revert_item_cb), (gpointer) entry->key);
662
 
  gtk_tooltips_set_tip (tooltips, widget, tip, NULL);
663
 
  if (alloc)
664
 
    free (tip);
665
 
 
666
 
  return widget;
667
 
}
668
 
 
669
 
static GtkWidget *create_item_editable (const xine_cfg_entry_t *entry)
670
 
{
671
 
  GtkWidget *widget;
672
 
 
673
 
  switch (entry->type)
674
 
  {
675
 
  case XINE_CONFIG_TYPE_ENUM:
676
 
    {
677
 
      int i;
678
 
      widget = gtk_combo_box_new_text ();
679
 
      for (i = 0; entry->enum_values[i]; ++i)
680
 
      {
681
 
        const char *label = gettext (entry->enum_values[i]);
682
 
        if (label == entry->enum_values[i])
683
 
          label = dgettext (LIB_PACKAGE, label);
684
 
        gtk_combo_box_append_text (GTK_COMBO_BOX(widget), label);
685
 
      }
686
 
      gtk_combo_box_set_active (GTK_COMBO_BOX(widget), entry->num_value);
687
 
      g_signal_connect (G_OBJECT(widget), "changed",
688
 
                        G_CALLBACK(enum_cb), strdup(entry->key));
689
 
    }
690
 
    break;
691
 
 
692
 
  case XINE_CONFIG_TYPE_STRING:
693
 
    if (entry->num_value)
694
 
    {
695
 
      GtkWidget *fc = gtk_file_chooser_dialog_new (entry->key, GTK_WINDOW (prefs_dialog),
696
 
                                            entry->num_value == 3 /* XINE_CONFIG_STRING_IS_DIRECTORY_NAME */
697
 
                                              ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
698
 
                                              : GTK_FILE_CHOOSER_ACTION_OPEN,
699
 
                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
700
 
                                            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
701
 
                                            NULL);
702
 
      widget = gtk_file_chooser_button_new_with_dialog (fc);
703
 
      gtk_file_chooser_set_local_only ((GtkFileChooser *) fc, TRUE);
704
 
      struct stat st;
705
 
      if (stat (entry->str_value, &st))
706
 
      {
707
 
        g_printerr (_("warning: configuration item %s points to a non-existent location %s\n"),
708
 
                  entry->key, entry->str_value);
709
 
        xine_log (xine, 0, _("warning: configuration item %s points to a non-existent location %s\n"),
710
 
                  entry->key, entry->str_value);
711
 
      }
712
 
      else
713
 
        gtk_file_chooser_set_filename ((GtkFileChooser *) fc, entry->str_value);
714
 
#ifdef GXINE_DEVICE_INFO
715
 
      gtk_file_chooser_set_use_preview_label ((GtkFileChooser *) fc, FALSE);
716
 
      GtkWidget *preview = g_object_new (GTK_TYPE_LABEL, "angle", 90.0,
717
 
                                         "selectable", FALSE, NULL);
718
 
      gtk_file_chooser_set_preview_widget ((GtkFileChooser *) fc, preview);
719
 
#endif
720
 
      g_object_connect (G_OBJECT(fc),
721
 
        "signal::response", G_CALLBACK(file_cb), widget,
722
 
        "signal::file-activated", G_CALLBACK(file_activate_cb), widget,
723
 
#ifdef GXINE_DEVICE_INFO
724
 
        "signal::update-preview", G_CALLBACK(file_preview_cb), widget,
725
 
        "signal::map", G_CALLBACK(file_preview_cb), fc,
726
 
#endif
727
 
        NULL);
728
 
      g_signal_connect_after (G_OBJECT(widget), "map", G_CALLBACK(file_map_cb), fc);
729
 
    }
730
 
    else
731
 
    {
732
 
      widget = gtk_entry_new();
733
 
      g_object_connect (G_OBJECT(widget),
734
 
        "signal::key-press-event", G_CALLBACK(entry_keypress_cb), (gchar *)entry->key,
735
 
        "signal::focus-out-event", G_CALLBACK(entry_cb), (gchar *)entry->key,
736
 
        NULL);
737
 
      gtk_entry_set_text (GTK_ENTRY(widget), entry->str_value);
738
 
    }
739
 
    g_object_set_data (G_OBJECT(widget), "cfg", (gpointer)entry->key);
740
 
 
741
 
    break;
742
 
 
743
 
  case XINE_CONFIG_TYPE_RANGE: /* slider */
744
 
    {
745
 
      GtkObject *adj = gtk_adjustment_new (entry->num_value, entry->range_min,
746
 
                                           entry->range_max, 1.0, 10.0, 0.0);
747
 
      widget = ui_hscale_new (adj, GTK_POS_TOP, 0);
748
 
      gtk_widget_set_size_request (widget, 128, -1);
749
 
      g_signal_connect (adj, "value-changed",
750
 
                        G_CALLBACK (range_cb), (gchar *)entry->key);
751
 
    }
752
 
    break;
753
 
 
754
 
  case XINE_CONFIG_TYPE_NUM:
755
 
    {
756
 
      GtkObject *adj = gtk_adjustment_new (entry->num_value, INT_MIN, INT_MAX,
757
 
                                           1, 10, 0);
758
 
      widget = ui_spin_button_new (adj);
759
 
      g_signal_connect (G_OBJECT(widget), "value-changed",
760
 
                        G_CALLBACK(spin_cb), (gchar *)entry->key);
761
 
    }
762
 
    break;
763
 
 
764
 
  case XINE_CONFIG_TYPE_BOOL:
765
 
    widget = gtk_check_button_new();
766
 
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(widget),
767
 
                                  entry->num_value == 1);
768
 
    g_signal_connect (G_OBJECT(widget), "toggled",
769
 
                      G_CALLBACK(check_box_cb), (gchar *)entry->key);
770
 
    /* gtk_misc_set_alignment (GTK_MISC(widget), 1.0, 1.0); */
771
 
    break;
772
 
 
773
 
  default:
774
 
    widget = gtk_label_new ("?");
775
 
    g_print (_("preferences: unknown type for entry ‘%s’\n"), entry->key);
776
 
  }
777
 
 
778
 
  g_datalist_set_data (&prefs_map, entry->key, widget);
779
 
  gtk_tooltips_set_tip (tooltips, widget, entry->help, NULL);
780
 
  return widget;
781
 
}
782
 
 
783
 
static GtkWidget *create_item_label (const xine_cfg_entry_t *entry)
784
 
{
785
 
  GtkWidget *label;
786
 
  char *labeltext;
787
 
  const char *labelkey = strrchr (entry->key, '.') + 1;
788
 
 
789
 
  if (entry->callback)
790
 
    labeltext = g_markup_printf_escaped ("<b>%s</b>\n%s",
791
 
                                         labelkey, entry->description);
792
 
  else
793
 
    labeltext = g_markup_printf_escaped ("<b>• <i>%s</i></b>\n%s",
794
 
                                         labelkey, entry->description);
795
 
 
796
 
  label = ui_label_new_with_markup (labeltext);
797
 
  free (labeltext);
798
 
 
799
 
  gtk_label_set_line_wrap (GTK_LABEL(label), TRUE);
800
 
  gtk_label_set_line_wrap_mode (GTK_LABEL(label), PANGO_WRAP_WORD);
801
 
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
802
 
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
803
 
 
804
 
  return label;
805
 
}
806
 
 
807
 
static GtkWidget *create_item_extra (const xine_cfg_entry_t *entry)
808
 
{
809
 
  switch (entry->type)
810
 
  {
811
 
  case XINE_CONFIG_TYPE_STRING:
812
 
#ifdef GXINE_DEVICE_INFO
813
 
    if (entry->num_value == 2 /* XINE_CONFIG_STRING_IS_DEVICE_NAME */)
814
 
      return gtk_label_new (NULL);
815
 
#endif
816
 
    break;
817
 
  }
818
 
  return NULL;
819
 
}
820
 
 
821
 
static GNode *create_item (const xine_cfg_entry_t *entry)
822
 
{
823
 
  pref_item_t *item = malloc (sizeof (pref_item_t));
824
 
  item->notebook = NULL;
825
 
  item->todefault = create_item_default (entry);
826
 
  item->revert = create_item_revert (entry);
827
 
  item->editable = create_item_editable (entry);
828
 
  item->label = create_item_label (entry);
829
 
  GtkWidget *extra = create_item_extra (entry);
830
 
  if (extra)
831
 
    g_object_set_data (G_OBJECT (item->editable), "extra", extra);
832
 
  item->separator = gtk_hseparator_new ();
833
 
  item->exp = entry->exp_level;
834
 
  return g_node_new (item);
835
 
}
836
 
 
837
 
static GNode *create_page (const char *key, size_t length)
838
 
{
839
 
  pref_page_t *page = malloc (sizeof (pref_page_t));
840
 
  page->notebook = GINT_TO_POINTER (1); /* dummy value - filled in later */
841
 
  page->page = page->table = NULL;
842
 
  page->prefix = g_strdup_printf ("%.*s", (int) length, key);
843
 
  page->label = strrchr (page->prefix, '.');
844
 
  if (page->label)
845
 
    ++page->label;
846
 
  else
847
 
    page->label = page->prefix;
848
 
  return g_node_new (page);
849
 
}
850
 
 
851
 
struct page_find_s {
852
 
  const char *key;
853
 
  size_t length;
854
 
  GNode *match;
855
 
};
856
 
 
857
 
static gboolean find_page_sub (GNode *node, struct page_find_s *find)
858
 
{
859
 
  const pref_page_t *pref = node->data;
860
 
  if (!pref->notebook)
861
 
    return FALSE;
862
 
  if (strncmp (pref->prefix, find->key, find->length) ||
863
 
      pref->prefix[find->length])
864
 
    return FALSE;
865
 
  find->match = node;
866
 
  return TRUE;
867
 
}
868
 
 
869
 
static GNode *find_page (const char *key, size_t length)
870
 
{
871
 
  struct page_find_s find = { key, length, NULL };
872
 
  g_node_traverse (prefs, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
873
 
                   (GNodeTraverseFunc) find_page_sub, &find);
874
 
  return find.match;
875
 
}
876
 
 
877
 
static gboolean unmix_mixed_page (GNode *node, gboolean *done)
878
 
{
879
 
  int types = 0;
880
 
  GNode *child, *new_page;
881
 
  char *prefix;
882
 
  pref_page_t *page = node->data;
883
 
 
884
 
  /* not a page? return */
885
 
  if (!page->notebook)
886
 
    return FALSE;
887
 
 
888
 
  /* check if this page has mixed content */
889
 
  for (child = g_node_first_child (node); child;
890
 
       child = g_node_next_sibling (child))
891
 
    types |= (PREF_T(child)->notebook ? 1 : 2);
892
 
 
893
 
  switch (types)
894
 
  {
895
 
  case 0:
896
 
    /* hmm, odd... found a blank page - delete it */
897
 
    logprintf ("gxine: prefs: eek, found a blank page '%s'\n", page->prefix);
898
 
    gtk_widget_destroy (page->notebook);
899
 
    free (page->prefix);
900
 
    free (page);
901
 
    g_node_destroy (node);
902
 
    *done = FALSE;
903
 
    return TRUE;
904
 
 
905
 
  case 3:
906
 
    /* found a mixed-mode page (this is normal) */
907
 
    prefix = g_strdup_printf ("%s. ", page->prefix);
908
 
    new_page = g_node_prepend (node, create_page (prefix, strlen (prefix)));
909
 
    free (prefix);
910
 
 
911
 
    /* move the new subpage's sibling item nodes into it */
912
 
    child = g_node_first_child (node);
913
 
    while (child)
914
 
    {
915
 
      GNode *next = g_node_next_sibling (child);
916
 
      pref_t *pref = child->data;
917
 
      if (!pref->notebook)
918
 
      {
919
 
        g_node_unlink (child);
920
 
        g_node_append (new_page, child);
921
 
      }
922
 
      child = next;
923
 
    }
924
 
 
925
 
    *done = FALSE;
926
 
    return TRUE;
927
 
  }
928
 
 
929
 
  return FALSE;
930
 
}
931
 
 
932
 
static gboolean focus_item_cb (GtkWidget *widget, GtkDirectionType *dir,
933
 
                               GNode *node)
934
 
{
935
 
  const pref_t *pref = node->data;
936
 
  const pref_t *parent = node->parent ? node->parent->data : NULL;
937
 
  GtkWidget *viewport = parent->page.table->parent;
938
 
  GtkAdjustment *adj = gtk_viewport_get_vadjustment ((GtkViewport *)viewport);
939
 
 
940
 
  int wt = pref->item.editable->allocation.y;
941
 
  int wb = wt + pref->item.editable->allocation.height;
942
 
  int lt = pref->item.label->allocation.y;
943
 
  int lb = lt + pref->item.label->allocation.height;
944
 
  int top = wt < lt ? wt : lt;
945
 
  int bottom = wb > lb ? wb : lb;
946
 
 
947
 
  if (top < adj->value)
948
 
    gtk_adjustment_set_value (adj, top);
949
 
  else if (bottom + 4 > adj->value + viewport->allocation.height)
950
 
    gtk_adjustment_set_value (adj, bottom - viewport->allocation.height + 4);
951
 
 
952
 
  return FALSE;
953
 
}
954
 
 
955
 
static gboolean put_content (GNode *node, gpointer data)
956
 
{
957
 
  pref_t *pref = node->data;
958
 
  pref_t *parent = node->parent ? node->parent->data : NULL;
959
 
 
960
 
  if (pref->notebook)
961
 
  {
962
 
    /* we have a page */
963
 
    pref_t *child = g_node_first_child (node)->data;
964
 
    GtkWidget *widget;
965
 
 
966
 
    if (child->notebook)
967
 
    {
968
 
      /* child pages present - create a notebook */
969
 
      widget = pref->page.notebook = pref->page.page = gtk_notebook_new ();
970
 
      gtk_notebook_set_scrollable (GTK_NOTEBOOK(widget), TRUE);
971
 
    }
972
 
    else
973
 
    {
974
 
      /* child pages not present - create a scrollable table */
975
 
      widget = pref->page.table = gtk_table_new (1, 1, FALSE);
976
 
      pref->page.page = gtk_scrolled_window_new (NULL, NULL);
977
 
      gtk_scrolled_window_set_policy
978
 
        (GTK_SCROLLED_WINDOW(pref->page.page),
979
 
                             GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
980
 
      gtk_scrolled_window_add_with_viewport
981
 
        (GTK_SCROLLED_WINDOW(pref->page.page), widget);
982
 
    }
983
 
 
984
 
    if (parent)
985
 
    {
986
 
      /* add this page to its parent's notebook */
987
 
      gtk_widget_ref (pref->page.page); /* hiding is done by removing */
988
 
      gtk_notebook_append_page (GTK_NOTEBOOK(parent->notebook), pref->page.page,
989
 
                                gtk_label_new (pref->page.label));
990
 
    }
991
 
    else
992
 
      gtk_widget_set_size_request (pref->page.page, 600, 350);
993
 
  }
994
 
  else
995
 
  {
996
 
    /* we have a config item */
997
 
    int r = g_node_child_position (node->parent, node) * 2;
998
 
    GtkTable *table = GTK_TABLE(parent->page.table);
999
 
    gtk_table_attach (table, pref->item.todefault, 0, 1, r, r + 1,
1000
 
                      GTK_FILL, GTK_SHRINK, 2, 5);
1001
 
    gtk_table_attach (table, pref->item.revert, 1, 2, r, r + 1,
1002
 
                      GTK_FILL, GTK_SHRINK, 2, 5);
1003
 
    GtkWidget *extra = g_object_get_data (G_OBJECT (pref->item.editable), "extra");
1004
 
    if (extra)
1005
 
    {
1006
 
      GtkBox *box = GTK_BOX (gtk_vbox_new (2, FALSE));
1007
 
      gtk_table_attach (table, (GtkWidget *) box, 2, 3, r, r + 1,
1008
 
                        GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 5);
1009
 
      gtk_box_pack_start_defaults (box, pref->item.editable);
1010
 
      gtk_box_pack_start_defaults (box, extra);
1011
 
#ifdef GXINE_DEVICE_INFO
1012
 
      xine_cfg_entry_t entry;
1013
 
      const char *key = g_object_get_data (G_OBJECT (pref->item.editable), "cfg");
1014
 
      if (xine_config_lookup_entry (xine, key, &entry))
1015
 
        display_hal_device_info (pref->item.editable, entry.str_value);
1016
 
#endif
1017
 
    }
1018
 
    else
1019
 
      gtk_table_attach (table, pref->item.editable, 2, 3, r, r + 1,
1020
 
                        GTK_EXPAND | GTK_FILL, GTK_SHRINK, 2, 5);
1021
 
    gtk_table_attach (table, pref->item.label, 3, 6, r, r + 1,
1022
 
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK, 5, 5);
1023
 
    gtk_table_attach (table, pref->item.separator, 0, 6, r + 1, r + 2,
1024
 
                      GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 5, 5);
1025
 
    g_signal_connect (pref->item.todefault, "focus",
1026
 
                      G_CALLBACK (focus_item_cb), node);
1027
 
    g_signal_connect (pref->item.revert, "focus",
1028
 
                      G_CALLBACK (focus_item_cb), node);
1029
 
    g_signal_connect (pref->item.editable, "focus",
1030
 
                      G_CALLBACK (focus_item_cb), node);
1031
 
  }
1032
 
 
1033
 
  return FALSE;
1034
 
}
1035
 
 
1036
 
static GtkWidget *make_prefs_window (void)
1037
 
{
1038
 
  xine_cfg_entry_t entry;
1039
 
  gboolean done;
1040
 
 
1041
 
  if (!xine_config_get_first_entry (xine, &entry))
1042
 
    return NULL;
1043
 
 
1044
 
  tooltips = gtk_tooltips_new ();
1045
 
  prefs = create_page ("", 0); /* root page */
1046
 
 
1047
 
  do
1048
 
  {
1049
 
    const char *point;
1050
 
    GNode *section;
1051
 
 
1052
 
    if (!entry.description ||
1053
 
        !(point= strrchr (entry.key, '.')) || point == entry.key)
1054
 
      continue;
1055
 
 
1056
 
    /* ensure that a page (window+table) is present for this config item */
1057
 
    section = find_page (entry.key, point - entry.key);
1058
 
    if (!section)
1059
 
    {
1060
 
      section = prefs;
1061
 
      point = entry.key - 1;
1062
 
      while ((point = strchr (point + 1, '.')))
1063
 
      {
1064
 
        GNode *self = find_page (entry.key, point - entry.key);
1065
 
        if (!self)
1066
 
        {
1067
 
          self = create_page (entry.key, point - entry.key);
1068
 
          g_node_append (section, self);
1069
 
        }
1070
 
        section = self;
1071
 
      }
1072
 
    }
1073
 
 
1074
 
    /* add the config item (and create its widgets) */
1075
 
    g_node_append (section, create_item (&entry));
1076
 
  } while (xine_config_get_next_entry (xine, &entry));
1077
 
 
1078
 
  /* we now have a full tree*/
1079
 
 
1080
 
  do
1081
 
  {
1082
 
    done = TRUE;
1083
 
    g_node_traverse (prefs, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1084
 
                     (GNodeTraverseFunc) unmix_mixed_page, &done);
1085
 
  } while (!done);
1086
 
 
1087
 
  g_node_traverse (prefs, G_PRE_ORDER, G_TRAVERSE_ALL, -1, put_content, NULL);
1088
 
 
1089
 
  gtk_tooltips_enable (tooltips);
1090
 
  return PREF_PAGE_T(prefs)->page;
1091
 
}
1092
 
 
1093
 
static void response_cb (GtkDialog *dbox, int response, gpointer data)
1094
 
{
1095
 
  gchar *fname;
1096
 
  switch (response)
1097
 
  {
1098
 
  case GTK_RESPONSE_ACCEPT:
1099
 
    fname = get_config_filename (FILE_CONFIG);
1100
 
    xine_config_save (xine, fname);
1101
 
    g_free (fname);
1102
 
    break;
1103
 
  default:
1104
 
    is_visible = 0;
1105
 
    gtk_widget_hide (prefs_dialog);
1106
 
  }
1107
 
}
1108
 
 
1109
 
static JSBool js_preferences_show (JSContext *cx, JSObject *obj, uintN argc,
1110
 
                                   jsval *argv, jsval *rval)
1111
 
{
1112
 
  /* se_t *se = (se_t *) JS_GetContextPrivate(cx); */
1113
 
  se_log_fncall_checkinit ("preferences_show");
1114
 
  preferences_show ();
1115
 
  return JS_TRUE;
1116
 
}
1117
 
 
1118
 
static void preferences_init_dbox (void)
1119
 
{
1120
 
  prefs_dialog = gtk_dialog_new_with_buttons (_("Preferences"), NULL, 0,
1121
 
                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1122
 
                                GTK_STOCK_CLOSE, GTK_RESPONSE_DELETE_EVENT,
1123
 
                                NULL);
1124
 
  gtk_window_set_default_size (GTK_WINDOW (prefs_dialog), 500, 150);
1125
 
  hide_on_delete (prefs_dialog, &is_visible);
1126
 
  g_signal_connect (G_OBJECT(prefs_dialog), "response",
1127
 
                    G_CALLBACK(response_cb), NULL);
1128
 
 
1129
 
  /* Make new tabbed box (notebook) */
1130
 
  GtkWidget *prefs_notebook = make_prefs_window ();
1131
 
  gtk_box_pack_start_defaults (GTK_BOX(GTK_DIALOG(prefs_dialog)->vbox),
1132
 
                               prefs_notebook);
1133
 
 
1134
 
  GtkWidget *w = ui_label_new_with_markup
1135
 
                   (_("Items marked “<b>• <i>like this</i></b>” require "
1136
 
                      "gxine to be restarted to take effect."));
1137
 
  gtk_box_pack_end (GTK_BOX(GTK_DIALOG(prefs_dialog)->vbox), w,
1138
 
                    FALSE, FALSE, 2);
1139
 
}
1140
 
 
1141
 
static gpointer preferences_first_show (gpointer unused)
1142
 
{
1143
 
  static int value = 0;
1144
 
  xine_cfg_entry_t entry;
1145
 
 
1146
 
  gtk_widget_show_all (GTK_DIALOG (prefs_dialog)->vbox);
1147
 
  if (xine_config_lookup_entry (xine, "gui.experience_level", &entry))
1148
 
    value = entry.num_value;
1149
 
 
1150
 
  select_show_pref_widgets (&value);
1151
 
  return NULL;
1152
 
}
1153
 
 
1154
 
void preferences_show (void)
1155
 
{
1156
 
  if (is_visible)
1157
 
  {
1158
 
    is_visible = FALSE;
1159
 
    gtk_widget_hide (prefs_dialog);
1160
 
  }
1161
 
  else
1162
 
  {
1163
 
    is_visible = TRUE;
1164
 
    static GOnce once = G_ONCE_INIT;
1165
 
    g_once (&once, preferences_first_show, NULL);
1166
 
    window_present (prefs_dialog, NULL);
1167
 
  }
1168
 
}
1169
 
 
1170
 
void preferences_init (void)
1171
 
{
1172
 
  is_visible = FALSE;
1173
 
  /* script engine functions */
1174
 
  se_defun (gse, NULL, "preferences_show", js_preferences_show, 0, 0,
1175
 
            SE_GROUP_DIALOGUE, NULL, NULL);
1176
 
 
1177
 
  preferences_init_dbox ();
1178
 
}
1179
 
 
1180
 
void preferences_update_entry (const xine_cfg_entry_t *entry)
1181
 
{
1182
 
  gpointer widget = g_datalist_get_data (&prefs_map, entry->key);
1183
 
 
1184
 
  if (widget)
1185
 
    switch (entry->type)
1186
 
    {
1187
 
    case XINE_CONFIG_TYPE_ENUM:
1188
 
      gtk_combo_box_set_active (widget, entry->num_value);
1189
 
      break;
1190
 
 
1191
 
    case XINE_CONFIG_TYPE_STRING:
1192
 
      if (entry->num_value)
1193
 
      {
1194
 
        gtk_file_chooser_set_filename (widget, entry->str_value);
1195
 
#ifdef GXINE_DEVICE_INFO
1196
 
        display_hal_device_info (widget, entry->str_value);
1197
 
#endif
1198
 
      }
1199
 
      else
1200
 
        gtk_entry_set_text (widget, entry->str_value);
1201
 
      break;
1202
 
 
1203
 
    case XINE_CONFIG_TYPE_RANGE: /* slider */
1204
 
      gtk_range_set_value (widget, entry->num_value);
1205
 
      break;
1206
 
 
1207
 
    case XINE_CONFIG_TYPE_NUM:
1208
 
      gtk_spin_button_set_value (widget, entry->num_value);
1209
 
      break;
1210
 
 
1211
 
    case XINE_CONFIG_TYPE_BOOL:
1212
 
      gtk_toggle_button_set_active (widget, entry->num_value == 1);
1213
 
      break;
1214
 
 
1215
 
    default:;
1216
 
    }
1217
 
 
1218
 
  xine_config_update_entry (xine, entry);
1219
 
}