Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
ui_preferences.c
Go to the documentation of this file.
1 /* Audacious - Cross-platform multimedia player
2  * Copyright (C) 2005-2011 Audacious development team.
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; under version 3 of the License.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses>.
15  *
16  * The Audacious team does not consider modular code linking to
17  * Audacious or using our public API to be a derived work.
18  */
19 
20 #include <string.h>
21 #include <stdio.h>
22 
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25 
26 #include <libaudcore/hook.h>
27 
28 #include "config.h"
29 #include "debug.h"
30 #include "gtk-compat.h"
31 #include "i18n.h"
32 #include "misc.h"
33 #include "output.h"
34 #include "playback.h"
35 #include "playlist.h"
36 #include "plugin.h"
37 #include "plugins.h"
38 #include "preferences.h"
39 #include "ui_preferences.h"
40 
41 #ifdef USE_CHARDET
42 #include <libguess.h>
43 #endif
44 
45 static void sw_volume_toggled (void);
46 
52 };
53 
54 typedef struct {
55  const char *icon_path;
56  const char *name;
57 } Category;
58 
59 typedef struct {
60  const char *name;
61  const char *tag;
63 
64 static /* GtkWidget * */ void * prefswin = NULL;
65 static GtkWidget *filepopup_settings = NULL;
66 static GtkWidget *category_treeview = NULL;
67 static GtkWidget *category_notebook = NULL;
68 GtkWidget *filepopupbutton = NULL;
69 
70 /* filepopup settings widgets */
73 GtkWidget *filepopup_recurse;
78 GtkWidget *filepopup_delay;
79 
80 /* prefswin widgets */
81 GtkWidget *titlestring_entry;
83 
84 static Category categories[] = {
85  {"audio.png", N_("Audio")},
86  {"connectivity.png", N_("Network")},
87  {"playlist.png", N_("Playlist")},
88  {"plugins.png", N_("Plugins")},
89 };
90 
91 static int n_categories = G_N_ELEMENTS(categories);
92 
94  { N_("Artist") , "${artist}" },
95  { N_("Album") , "${album}" },
96  { N_("Title") , "${title}" },
97  { N_("Tracknumber"), "${track-number}" },
98  { N_("Genre") , "${genre}" },
99  { N_("Filename") , "${file-name}" },
100  { N_("Filepath") , "${file-path}" },
101  { N_("Date") , "${date}" },
102  { N_("Year") , "${year}" },
103  { N_("Comment") , "${comment}" },
104  { N_("Codec") , "${codec}" },
105  { N_("Quality") , "${quality}" },
106 };
107 static const unsigned int n_title_field_tags = G_N_ELEMENTS(title_field_tags);
108 
109 #ifdef USE_CHARDET
110 static ComboBoxElements chardet_detector_presets[] = {
111  {"", N_("None")},
112  {GUESS_REGION_AR, N_("Arabic")},
113  {GUESS_REGION_BL, N_("Baltic")},
114  {GUESS_REGION_CN, N_("Chinese")},
115  {GUESS_REGION_GR, N_("Greek")},
116  {GUESS_REGION_HW, N_("Hebrew")},
117  {GUESS_REGION_JP, N_("Japanese")},
118  {GUESS_REGION_KR, N_("Korean")},
119  {GUESS_REGION_PL, N_("Polish")},
120  {GUESS_REGION_RU, N_("Russian")},
121  {GUESS_REGION_TW, N_("Taiwanese")},
122  {GUESS_REGION_TR, N_("Turkish")}};
123 #endif
124 
126  { GINT_TO_POINTER(16), "16" },
127  { GINT_TO_POINTER(24), "24" },
128  { GINT_TO_POINTER(32), "32" },
129  {GINT_TO_POINTER (0), "Floating point"},
130 };
131 
132 typedef struct {
133  void *next;
134  GtkWidget *container;
135  const char * pg_name;
136  const char * img_url;
138 
140 
141 static void * create_output_plugin_box (void);
142 
143 static PreferencesWidget rg_mode_widgets[] = {
144  {WIDGET_CHK_BTN, N_("Album mode"), .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}};
145 
146 static PreferencesWidget audio_page_widgets[] = {
147  {WIDGET_LABEL, N_("<b>Output Settings</b>")},
148  {WIDGET_CUSTOM, .data = {.populate = create_output_plugin_box}},
149  {WIDGET_COMBO_BOX, N_("Bit depth:"),
150  .cfg_type = VALUE_INT, .cname = "output_bit_depth",
151  .data = {.combo = {bitdepth_elements, G_N_ELEMENTS (bitdepth_elements), TRUE}}},
152  {WIDGET_SPIN_BTN, N_("Buffer size:"),
153  .cfg_type = VALUE_INT, .cname = "output_buffer_size",
154  .data = {.spin_btn = {100, 10000, 1000, N_("ms")}}},
155  {WIDGET_CHK_BTN, N_("Use software volume control (not recommended)"),
156  .cfg_type = VALUE_BOOLEAN, .cname = "software_volume_control", .callback = sw_volume_toggled},
157  {WIDGET_LABEL, N_("<b>Replay Gain</b>")},
158  {WIDGET_CHK_BTN, N_("Enable Replay Gain"),
159  .cfg_type = VALUE_BOOLEAN, .cname = "enable_replay_gain"},
160  {WIDGET_BOX, .child = TRUE, .data = {.box = {rg_mode_widgets, G_N_ELEMENTS (rg_mode_widgets), TRUE}}},
161  {WIDGET_CHK_BTN, N_("Prevent clipping (recommended)"), .child = TRUE,
162  .cfg_type = VALUE_BOOLEAN, .cname = "enable_clipping_prevention"},
163  {WIDGET_LABEL, N_("<b>Adjust Levels</b>"), .child = TRUE},
164  {WIDGET_SPIN_BTN, N_("Amplify all files:"), .child = TRUE,
165  .cfg_type = VALUE_FLOAT, .cname = "replay_gain_preamp",
166  .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}},
167  {WIDGET_SPIN_BTN, N_("Amplify untagged files:"), .child = TRUE,
168  .cfg_type = VALUE_FLOAT, .cname = "default_gain",
169  .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}};
170 
171 static PreferencesWidget proxy_host_port_elements[] = {
172  {WIDGET_ENTRY, N_("Proxy hostname:"), .cfg_type = VALUE_STRING, .cname = "proxy_host"},
173  {WIDGET_ENTRY, N_("Proxy port:"), .cfg_type = VALUE_STRING, .cname = "proxy_port"}};
174 
175 static PreferencesWidget proxy_auth_elements[] = {
176  {WIDGET_ENTRY, N_("Proxy username:"), .cfg_type = VALUE_STRING, .cname = "proxy_user"},
177  {WIDGET_ENTRY, N_("Proxy password:"), .cfg_type = VALUE_STRING, .cname = "proxy_pass",
178  .data = {.entry = {.password = TRUE}}}};
179 
180 static PreferencesWidget connectivity_page_widgets[] = {
181  {WIDGET_LABEL, N_("<b>Proxy Configuration</b>"), NULL, NULL, NULL, FALSE},
182  {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"},
183  {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_host_port_elements,
184  G_N_ELEMENTS (proxy_host_port_elements)}}},
185  {WIDGET_CHK_BTN, N_("Use authentication with proxy"),
186  .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"},
187  {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_auth_elements,
188  G_N_ELEMENTS (proxy_auth_elements)}}}
189 };
190 
191 static PreferencesWidget chardet_elements[] = {
192 #ifdef USE_CHARDET
193  {WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"),
194  .cfg_type = VALUE_STRING, .cname = "chardet_detector", .child = TRUE,
195  .data = {.combo = {chardet_detector_presets,
196  G_N_ELEMENTS (chardet_detector_presets), TRUE}}},
197 #endif
198  {WIDGET_ENTRY, N_("Fallback character encodings:"), .cfg_type = VALUE_STRING,
199  .cname = "chardet_fallback", .child = TRUE}};
200 
201 static PreferencesWidget playlist_page_widgets[] = {
202  {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE},
203  {WIDGET_CHK_BTN, N_("Continue playback on startup"),
204  .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"},
205  {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"),
206  .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"},
207  {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"),
208  .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"},
209  {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"),
210  .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"},
211  {WIDGET_LABEL, N_("<b>Metadata</b>"), NULL, NULL, NULL, FALSE},
212  {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"),
213  .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play",
214  .callback = playlist_trigger_scan},
215  {WIDGET_TABLE, .data = {.table = {chardet_elements,
216  G_N_ELEMENTS (chardet_elements)}}}
217 };
218 
219 #define TITLESTRING_NPRESETS 6
220 
221 static const char * const titlestring_presets[TITLESTRING_NPRESETS] = {
222  "${title}",
223  "${?artist:${artist} - }${title}",
224  "${?artist:${artist} - }${?album:${album} - }${title}",
225  "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}",
226  "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}",
227  "${?album:${album} - }${title}"};
228 
229 static const char * const titlestring_preset_names[TITLESTRING_NPRESETS] = {
230  N_("TITLE"),
231  N_("ARTIST - TITLE"),
232  N_("ARTIST - ALBUM - TITLE"),
233  N_("ARTIST - ALBUM - TRACK. TITLE"),
234  N_("ARTIST [ ALBUM ] - TRACK. TITLE"),
235  N_("ALBUM - TITLE")};
236 
238 
239 static void
240 change_category(GtkNotebook * notebook,
241  GtkTreeSelection * selection)
242 {
243  GtkTreeModel *model;
244  GtkTreeIter iter;
245  int index;
246 
247  if (!gtk_tree_selection_get_selected(selection, &model, &iter))
248  return;
249 
250  gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
251  gtk_notebook_set_current_page(notebook, index);
252 }
253 
254 static void
255 editable_insert_text(GtkEditable * editable,
256  const char * text,
257  int * pos)
258 {
259  gtk_editable_insert_text(editable, text, strlen(text), pos);
260 }
261 
262 static void
263 titlestring_tag_menu_callback(GtkMenuItem * menuitem,
264  gpointer data)
265 {
266  const char *separator = " - ";
267  int item = GPOINTER_TO_INT(data);
268  int pos;
269 
270  pos = gtk_editable_get_position(GTK_EDITABLE(titlestring_entry));
271 
272  /* insert separator as needed */
273  if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(titlestring_entry)), -1) > 0)
274  editable_insert_text(GTK_EDITABLE(titlestring_entry), separator, &pos);
275 
276  editable_insert_text(GTK_EDITABLE(titlestring_entry), _(title_field_tags[item].tag),
277  &pos);
278 
279  gtk_editable_set_position(GTK_EDITABLE(titlestring_entry), pos);
280 }
281 
282 static void
284  gpointer data)
285 {
286  GtkMenu * menu = data;
287  gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
288 }
289 
290 static void update_titlestring_cbox (GtkComboBox * cbox, const char * format)
291 {
292  int preset;
293  for (preset = 0; preset < TITLESTRING_NPRESETS; preset ++)
294  {
295  if (! strcmp (titlestring_presets[preset], format))
296  break;
297  }
298 
299  if (gtk_combo_box_get_active (cbox) != preset)
300  gtk_combo_box_set_active (cbox, preset);
301 }
302 
303 static void on_titlestring_entry_changed (GtkEntry * entry, GtkComboBox * cbox)
304 {
305  const char * format = gtk_entry_get_text (entry);
306  set_string (NULL, "generic_title_format", format);
307  update_titlestring_cbox (cbox, format);
309 }
310 
311 static void on_titlestring_cbox_changed (GtkComboBox * cbox, GtkEntry * entry)
312 {
313  int preset = gtk_combo_box_get_active (cbox);
314  if (preset < TITLESTRING_NPRESETS)
315  gtk_entry_set_text (entry, titlestring_presets[preset]);
316 }
317 
318 static void widget_set_bool (PreferencesWidget * widget, bool_t value)
319 {
320  g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN);
321 
322  if (widget->cfg)
323  * (bool_t *) widget->cfg = value;
324  else if (widget->cname)
325  set_bool (widget->csect, widget->cname, value);
326 
327  if (widget->callback)
328  widget->callback ();
329 }
330 
331 static bool_t widget_get_bool (PreferencesWidget * widget)
332 {
333  g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE);
334 
335  if (widget->cfg)
336  return * (bool_t *) widget->cfg;
337  else if (widget->cname)
338  return get_bool (widget->csect, widget->cname);
339  else
340  return FALSE;
341 }
342 
343 static void widget_set_int (PreferencesWidget * widget, int value)
344 {
345  g_return_if_fail (widget->cfg_type == VALUE_INT);
346 
347  if (widget->cfg)
348  * (int *) widget->cfg = value;
349  else if (widget->cname)
350  set_int (widget->csect, widget->cname, value);
351 
352  if (widget->callback)
353  widget->callback ();
354 }
355 
356 static int widget_get_int (PreferencesWidget * widget)
357 {
358  g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0);
359 
360  if (widget->cfg)
361  return * (int *) widget->cfg;
362  else if (widget->cname)
363  return get_int (widget->csect, widget->cname);
364  else
365  return 0;
366 }
367 
368 static void widget_set_double (PreferencesWidget * widget, double value)
369 {
370  g_return_if_fail (widget->cfg_type == VALUE_FLOAT);
371 
372  if (widget->cfg)
373  * (float *) widget->cfg = value;
374  else if (widget->cname)
375  set_double (widget->csect, widget->cname, value);
376 
377  if (widget->callback)
378  widget->callback ();
379 }
380 
381 static double widget_get_double (PreferencesWidget * widget)
382 {
383  g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0);
384 
385  if (widget->cfg)
386  return * (float *) widget->cfg;
387  else if (widget->cname)
388  return get_double (widget->csect, widget->cname);
389  else
390  return 0;
391 }
392 
393 static void widget_set_string (PreferencesWidget * widget, const char * value)
394 {
395  g_return_if_fail (widget->cfg_type == VALUE_STRING);
396 
397  if (widget->cfg)
398  {
399  g_free (* (char * *) widget->cfg);
400  * (char * *) widget->cfg = g_strdup (value);
401  }
402  else if (widget->cname)
403  set_string (widget->csect, widget->cname, value);
404 
405  if (widget->callback)
406  widget->callback ();
407 }
408 
409 static char * widget_get_string (PreferencesWidget * widget)
410 {
411  g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL);
412 
413  if (widget->cfg)
414  return g_strdup (* (char * *) widget->cfg);
415  else if (widget->cname)
416  return get_string (widget->csect, widget->cname);
417  else
418  return NULL;
419 }
420 
421 static void on_font_btn_font_set (GtkFontButton * button, PreferencesWidget * widget)
422 {
423  widget_set_string (widget, gtk_font_button_get_font_name (button));
424 }
425 
426 static void
427 plugin_preferences_ok(GtkWidget *widget, PluginPreferences *settings)
428 {
429  if (settings->apply)
430  settings->apply();
431 
432  gtk_widget_destroy(GTK_WIDGET(settings->data));
433 }
434 
435 static void
436 plugin_preferences_apply(GtkWidget *widget, PluginPreferences *settings)
437 {
438  if (settings->apply)
439  settings->apply();
440 }
441 
442 static void
443 plugin_preferences_cancel(GtkWidget *widget, PluginPreferences *settings)
444 {
445  if (settings->cancel)
446  settings->cancel();
447 
448  gtk_widget_destroy(GTK_WIDGET(settings->data));
449 }
450 
451 static void plugin_preferences_destroy(GtkWidget *widget, PluginPreferences *settings)
452 {
453  gtk_widget_destroy(widget);
454 
455  if (settings->cleanup)
456  settings->cleanup();
457 
458  settings->data = NULL;
459 }
460 
461 void plugin_preferences_show (PluginPreferences * settings)
462 {
463  GtkWidget *window;
464  GtkWidget *vbox, *bbox, *ok, *apply, *cancel;
465 
466  if (settings->data != NULL) {
467  gtk_widget_show(GTK_WIDGET(settings->data));
468  return;
469  }
470 
471  if (settings->init)
472  settings->init();
473 
474  const char * d = settings->domain;
475  if (! d)
476  {
477  printf ("WARNING: PluginPreferences window with title \"%s\" did not "
478  "declare its gettext domain. Text may not be translated correctly.\n",
479  settings->title);
480  d = "audacious-plugins";
481  }
482 
483  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
484  gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
485 
486  if (settings->title)
487  gtk_window_set_title ((GtkWindow *) window, dgettext (d, settings->title));
488 
489  gtk_container_set_border_width(GTK_CONTAINER(window), 10);
490  g_signal_connect(G_OBJECT(window), "destroy",
491  G_CALLBACK(plugin_preferences_destroy), settings);
492 
493  vbox = gtk_vbox_new(FALSE, 10);
494  create_widgets_with_domain ((GtkBox *) vbox, settings->prefs,
495  settings->n_prefs, d);
496  gtk_container_add(GTK_CONTAINER(window), vbox);
497 
498  bbox = gtk_hbutton_box_new();
499  gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
500  gtk_box_set_spacing(GTK_BOX(bbox), 5);
501  gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
502 
503  ok = gtk_button_new_from_stock(GTK_STOCK_OK);
504  g_signal_connect(G_OBJECT(ok), "clicked",
505  G_CALLBACK(plugin_preferences_ok), settings);
506  gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
508  gtk_widget_grab_default(ok);
509 
510  apply = gtk_button_new_from_stock(GTK_STOCK_APPLY);
511  g_signal_connect(G_OBJECT(apply), "clicked",
512  G_CALLBACK(plugin_preferences_apply), settings);
513  gtk_box_pack_start(GTK_BOX(bbox), apply, TRUE, TRUE, 0);
514 
515  cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
516  g_signal_connect(G_OBJECT(cancel), "clicked",
517  G_CALLBACK(plugin_preferences_cancel), settings);
518  gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
519 
520  gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(prefswin));
521  gtk_widget_show_all(window);
522  settings->data = (gpointer)window;
523 }
524 
525 void plugin_preferences_cleanup (PluginPreferences * p)
526 {
527  if (p->data != NULL)
528  {
529  gtk_widget_destroy (p->data);
530  p->data = NULL;
531  }
532 }
533 
534 static void on_spin_btn_changed_int (GtkSpinButton * button, PreferencesWidget * widget)
535 {
536  widget_set_int (widget, gtk_spin_button_get_value_as_int (button));
537 }
538 
539 static void on_spin_btn_changed_float (GtkSpinButton * button, PreferencesWidget * widget)
540 {
541  widget_set_double (widget, gtk_spin_button_get_value (button));
542 }
543 
544 static void fill_category_list (GtkTreeView * treeview, GtkNotebook * notebook)
545 {
546  GtkListStore *store;
547  GtkCellRenderer *renderer;
548  GtkTreeViewColumn *column;
549  GtkTreeSelection *selection;
550  GtkTreeIter iter;
551  GdkPixbuf *img;
552  CategoryQueueEntry *qlist;
553  int i;
554 
555  column = gtk_tree_view_column_new();
556  gtk_tree_view_column_set_title(column, _("Category"));
557  gtk_tree_view_append_column(treeview, column);
558  gtk_tree_view_column_set_spacing(column, 2);
559 
560  renderer = gtk_cell_renderer_pixbuf_new();
561  gtk_tree_view_column_pack_start(column, renderer, FALSE);
562  gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", 0, NULL);
563 
564  renderer = gtk_cell_renderer_text_new();
565  gtk_tree_view_column_pack_start(column, renderer, FALSE);
566  gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
567 
568  g_object_set ((GObject *) renderer, "wrap-width", 96, "wrap-mode",
569  PANGO_WRAP_WORD_CHAR, NULL);
570 
571  store = gtk_list_store_new(CATEGORY_VIEW_N_COLS,
572  GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
573  gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
574 
575  for (i = 0; i < n_categories; i ++)
576  {
577  char * path = g_strdup_printf ("%s/images/%s",
578  get_path (AUD_PATH_DATA_DIR), categories[i].icon_path);
579  img = gdk_pixbuf_new_from_file (path, NULL);
580  g_free (path);
581 
582  gtk_list_store_append(store, &iter);
583  gtk_list_store_set(store, &iter,
586  gettext(categories[i].name), CATEGORY_VIEW_COL_ID,
587  i, -1);
588  g_object_unref(img);
589  }
590 
591  selection = gtk_tree_view_get_selection(treeview);
592 
593  g_signal_connect_swapped(selection, "changed",
594  G_CALLBACK(change_category), notebook);
595 
596  /* mark the treeview widget as available to third party plugins */
597  category_treeview = GTK_WIDGET(treeview);
598 
599  /* prefswin_page_queue_destroy already pops the queue forward for us. */
600  for (qlist = category_queue; qlist != NULL; qlist = category_queue)
601  {
602  CategoryQueueEntry *ent = (CategoryQueueEntry *) qlist;
603 
604  prefswin_page_new(ent->container, ent->pg_name, ent->img_url);
606  }
607 }
608 
609 static void on_show_filepopup_toggled (GtkToggleButton * button)
610 {
611  bool_t active = gtk_toggle_button_get_active (button);
612  set_bool (NULL, "show_filepopup_for_tuple", active);
613  gtk_widget_set_sensitive (filepopup_settings_button, active);
614 }
615 
617 {
618  char * string = get_string (NULL, "cover_name_include");
619  gtk_entry_set_text ((GtkEntry *) filepopup_cover_name_include, string);
620  g_free (string);
621 
622  string = get_string (NULL, "cover_name_exclude");
623  gtk_entry_set_text ((GtkEntry *) filepopup_cover_name_exclude, string);
624  g_free (string);
625 
626  gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_recurse,
627  get_bool (NULL, "recurse_for_cover"));
628  gtk_spin_button_set_value ((GtkSpinButton *) filepopup_recurse_depth,
629  get_int (NULL, "recurse_for_cover_depth"));
630  gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_use_file_cover,
631  get_bool (NULL, "use_file_cover"));
632 
633  gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_showprogressbar,
634  get_bool (NULL, "filepopup_showprogressbar"));
635  gtk_spin_button_set_value ((GtkSpinButton *) filepopup_delay,
636  get_int (NULL, "filepopup_delay"));
637 
638  gtk_widget_show (filepopup_settings);
639 }
640 
641 static void on_filepopup_ok_clicked (void)
642 {
643  set_string (NULL, "cover_name_include",
644  gtk_entry_get_text ((GtkEntry *) filepopup_cover_name_include));
645  set_string (NULL, "cover_name_exclude",
646  gtk_entry_get_text ((GtkEntry *) filepopup_cover_name_exclude));
647 
648  set_bool (NULL, "recurse_for_cover",
649  gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_recurse));
650  set_int (NULL, "recurse_for_cover_depth",
651  gtk_spin_button_get_value_as_int ((GtkSpinButton *) filepopup_recurse_depth));
652  set_bool (NULL, "use_file_cover",
653  gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_use_file_cover));
654 
655  set_bool (NULL, "filepopup_showprogressbar",
656  gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_showprogressbar));
657  set_int (NULL, "filepopup_delay",
658  gtk_spin_button_get_value_as_int ((GtkSpinButton *) filepopup_delay));
659 
660  gtk_widget_hide (filepopup_settings);
661 }
662 
663 static void
664 on_filepopup_cancel_clicked(GtkButton *button, gpointer data)
665 {
666  gtk_widget_hide(filepopup_settings);
667 }
668 
669 static void on_toggle_button_toggled (GtkToggleButton * button, PreferencesWidget * widget)
670 {
671  bool_t active = gtk_toggle_button_get_active (button);
672  widget_set_bool (widget, active);
673 
674  GtkWidget * child = g_object_get_data ((GObject *) button, "child");
675  if (child)
676  gtk_widget_set_sensitive (child, active);
677 }
678 
679 static void init_toggle_button (GtkWidget * button, PreferencesWidget * widget)
680 {
681  if (widget->cfg_type != VALUE_BOOLEAN)
682  return;
683 
684  gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget));
685  g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, widget);
686 }
687 
688 static void on_entry_changed (GtkEntry * entry, PreferencesWidget * widget)
689 {
690  widget_set_string (widget, gtk_entry_get_text (entry));
691 }
692 
693 static void on_cbox_changed_int (GtkComboBox * combobox, PreferencesWidget * widget)
694 {
695  int position = gtk_combo_box_get_active (combobox);
696  widget_set_int (widget, GPOINTER_TO_INT (widget->data.combo.elements[position].value));
697 }
698 
699 static void on_cbox_changed_string (GtkComboBox * combobox, PreferencesWidget * widget)
700 {
701  int position = gtk_combo_box_get_active (combobox);
702  widget_set_string (widget, widget->data.combo.elements[position].value);
703 }
704 
705 static void fill_cbox (GtkWidget * combobox, PreferencesWidget * widget, const char * domain)
706 {
707  for (int i = 0; i < widget->data.combo.n_elements; i ++)
709  dgettext (domain, widget->data.combo.elements[i].label));
710 
711  switch (widget->cfg_type)
712  {
713  case VALUE_INT:;
714  int ivalue = widget_get_int (widget);
715 
716  for (int i = 0; i < widget->data.combo.n_elements; i++)
717  {
718  if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue)
719  {
720  gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
721  break;
722  }
723  }
724 
725  g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget);
726  break;
727 
728  case VALUE_STRING:;
729  char * value = widget_get_string (widget);
730 
731  for(int i = 0; i < widget->data.combo.n_elements; i++)
732  {
733  if (value && ! strcmp (widget->data.combo.elements[i].value, value))
734  {
735  gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
736  break;
737  }
738  }
739 
740  g_free (value);
741 
742  g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget);
743  break;
744 
745  default:
746  break;
747  }
748 }
749 
750 void
752 {
753  GtkWidget *vbox;
754  GtkWidget *table;
755 
756  GtkWidget *label_cover_retrieve;
757  GtkWidget *label_cover_search;
758  GtkWidget *label_exclude;
759  GtkWidget *label_include;
760  GtkWidget *label_search_depth;
761  GtkWidget *label_misc;
762  GtkWidget *label_delay;
763 
764  GtkAdjustment *recurse_for_cover_depth_adj;
765  GtkAdjustment *delay_adj;
766  GtkWidget *alignment;
767 
768  GtkWidget *hbox;
769  GtkWidget *hbuttonbox;
770  GtkWidget *btn_cancel;
771  GtkWidget *btn_ok;
772 
773  filepopup_settings = gtk_window_new(GTK_WINDOW_TOPLEVEL);
774  gtk_container_set_border_width(GTK_CONTAINER(filepopup_settings), 12);
775  gtk_window_set_title(GTK_WINDOW(filepopup_settings), _("Popup Information Settings"));
776  gtk_window_set_position(GTK_WINDOW(filepopup_settings), GTK_WIN_POS_CENTER_ON_PARENT);
777  gtk_window_set_skip_taskbar_hint(GTK_WINDOW(filepopup_settings), TRUE);
778  gtk_window_set_type_hint(GTK_WINDOW(filepopup_settings), GDK_WINDOW_TYPE_HINT_DIALOG);
779  gtk_window_set_transient_for(GTK_WINDOW(filepopup_settings), GTK_WINDOW(prefswin));
780 
781  vbox = gtk_vbox_new(FALSE, 12);
782  gtk_container_add(GTK_CONTAINER(filepopup_settings), vbox);
783 
784  label_cover_retrieve = gtk_label_new(_("<b>Cover image retrieve</b>"));
785  gtk_box_pack_start(GTK_BOX(vbox), label_cover_retrieve, FALSE, FALSE, 0);
786  gtk_label_set_use_markup(GTK_LABEL(label_cover_retrieve), TRUE);
787  gtk_misc_set_alignment(GTK_MISC(label_cover_retrieve), 0, 0.5);
788 
789  label_cover_search = gtk_label_new(_("While searching for the album's cover, Audacious looks for certain words in the filename. You can specify those words in the lists below, separated using commas."));
790  gtk_box_pack_start(GTK_BOX(vbox), label_cover_search, FALSE, FALSE, 0);
791  gtk_label_set_line_wrap(GTK_LABEL(label_cover_search), TRUE);
792  gtk_misc_set_alignment(GTK_MISC(label_cover_search), 0, 0);
793  gtk_misc_set_padding(GTK_MISC(label_cover_search), 12, 0);
794 
795  table = gtk_table_new(2, 2, FALSE);
796  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
797  gtk_table_set_row_spacings(GTK_TABLE(table), 4);
798  gtk_table_set_col_spacings(GTK_TABLE(table), 4);
799 
800  filepopup_cover_name_include = gtk_entry_new();
801  gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_include, 1, 2, 0, 1,
802  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
803  (GtkAttachOptions) (0), 0, 0);
804  gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_include), TRUE);
805 
806  label_exclude = gtk_label_new(_("Exclude:"));
807  gtk_table_attach(GTK_TABLE(table), label_exclude, 0, 1, 1, 2,
808  (GtkAttachOptions) (0),
809  (GtkAttachOptions) (0), 0, 0);
810  gtk_misc_set_alignment(GTK_MISC(label_exclude), 0, 0.5);
811  gtk_misc_set_padding(GTK_MISC(label_exclude), 12, 0);
812 
813  label_include = gtk_label_new(_("Include:"));
814  gtk_table_attach(GTK_TABLE(table), label_include, 0, 1, 0, 1,
815  (GtkAttachOptions) (0),
816  (GtkAttachOptions) (0), 0, 0);
817  gtk_misc_set_alignment(GTK_MISC(label_include), 0, 0.5);
818  gtk_misc_set_padding(GTK_MISC(label_include), 12, 0);
819 
820  filepopup_cover_name_exclude = gtk_entry_new();
821  gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_exclude, 1, 2, 1, 2,
822  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
823  (GtkAttachOptions) (0), 0, 0);
824  gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_exclude), TRUE);
825 
826  alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
827  gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
828  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
829 
830  filepopup_recurse = gtk_check_button_new_with_mnemonic(_("Recursively search for cover"));
831  gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse);
832 
833  alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
834  gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
835  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 45, 0);
836 
837  filepopup_recurse_depth_box = gtk_hbox_new(FALSE, 0);
838  gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse_depth_box);
839 
840  label_search_depth = gtk_label_new(_("Search depth: "));
841  gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), label_search_depth, TRUE, TRUE, 0);
842  gtk_misc_set_padding(GTK_MISC(label_search_depth), 4, 0);
843 
844  recurse_for_cover_depth_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0,
845  100, 1, 10, 0);
846  filepopup_recurse_depth = gtk_spin_button_new(GTK_ADJUSTMENT(recurse_for_cover_depth_adj), 1, 0);
847  gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), filepopup_recurse_depth, TRUE, TRUE, 0);
848  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_recurse_depth), TRUE);
849 
850  alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
851  gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
852  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
853 
854  filepopup_use_file_cover = gtk_check_button_new_with_mnemonic(_("Use per-file cover"));
855  gtk_container_add(GTK_CONTAINER(alignment), filepopup_use_file_cover);
856 
857  label_misc = gtk_label_new(_("<b>Miscellaneous</b>"));
858  gtk_box_pack_start(GTK_BOX(vbox), label_misc, FALSE, FALSE, 0);
859  gtk_label_set_use_markup(GTK_LABEL(label_misc), TRUE);
860  gtk_misc_set_alignment(GTK_MISC(label_misc), 0, 0.5);
861 
862  alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
863  gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
864  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
865 
866  filepopup_showprogressbar = gtk_check_button_new_with_mnemonic(_("Show Progress bar for the current track"));
867  gtk_container_add(GTK_CONTAINER(alignment), filepopup_showprogressbar);
868 
869  alignment = gtk_alignment_new(0, 0.5, 1, 1);
870  gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
871  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
872 
873  hbox = gtk_hbox_new(FALSE, 0);
874  gtk_container_add(GTK_CONTAINER(alignment), hbox);
875 
876  label_delay = gtk_label_new(_("Delay until filepopup comes up: "));
877  gtk_box_pack_start(GTK_BOX(hbox), label_delay, TRUE, TRUE, 0);
878  gtk_misc_set_alignment(GTK_MISC(label_delay), 0, 0.5);
879  gtk_misc_set_padding(GTK_MISC(label_delay), 12, 0);
880 
881  delay_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 100, 1, 10, 0);
882  filepopup_delay = gtk_spin_button_new(GTK_ADJUSTMENT(delay_adj), 1, 0);
883  gtk_box_pack_start(GTK_BOX(hbox), filepopup_delay, TRUE, TRUE, 0);
884  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_delay), TRUE);
885 
886  hbuttonbox = gtk_hbutton_box_new();
887  gtk_box_pack_start(GTK_BOX(vbox), hbuttonbox, FALSE, FALSE, 0);
888  gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox), GTK_BUTTONBOX_END);
889  gtk_box_set_spacing(GTK_BOX(hbuttonbox), 6);
890 
891  btn_cancel = gtk_button_new_from_stock("gtk-cancel");
892  gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_cancel);
893 
894  btn_ok = gtk_button_new_from_stock("gtk-ok");
895  gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_ok);
897 
898  g_signal_connect(G_OBJECT(filepopup_settings), "delete_event",
899  G_CALLBACK(gtk_widget_hide_on_delete),
900  NULL);
901  g_signal_connect(G_OBJECT(btn_cancel), "clicked",
902  G_CALLBACK(on_filepopup_cancel_clicked),
903  NULL);
904  g_signal_connect(G_OBJECT(btn_ok), "clicked",
905  G_CALLBACK(on_filepopup_ok_clicked),
906  NULL);
907 
908  gtk_widget_grab_default(btn_ok);
909  gtk_widget_show_all(vbox);
910 }
911 
912 static void create_spin_button (PreferencesWidget * widget, GtkWidget * *
913  label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char *
914  domain)
915 {
916  g_return_if_fail(widget->type == WIDGET_SPIN_BTN);
917 
918  * label_pre = gtk_label_new (dgettext (domain, widget->label));
919 
920  *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min,
921  widget->data.spin_btn.max,
922  widget->data.spin_btn.step);
923 
924 
925  if (widget->tooltip)
926  gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain,
927  widget->tooltip));
928 
929  if (widget->data.spin_btn.right_label) {
930  * label_past = gtk_label_new (dgettext (domain,
931  widget->data.spin_btn.right_label));
932  }
933 
934  switch (widget->cfg_type)
935  {
936  case VALUE_INT:
937  gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
938  g_signal_connect (* spin_btn, "value_changed", (GCallback) on_spin_btn_changed_int, widget);
939  break;
940  case VALUE_FLOAT:
941  gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
942  g_signal_connect (* spin_btn, "value_changed", (GCallback)
943  on_spin_btn_changed_float, widget);
944  break;
945  default:
946  break;
947  }
948 }
949 
950 void create_font_btn (PreferencesWidget * widget, GtkWidget * * label,
951  GtkWidget * * font_btn, const char * domain)
952 {
953  *font_btn = gtk_font_button_new();
954  gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE);
955  gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE);
956  if (widget->label) {
957  * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
958  gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
959  gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5);
960  gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
961  gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn);
962  }
963 
964  if (widget->data.font_btn.title)
965  gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn),
966  dgettext (domain, widget->data.font_btn.title));
967 
968  char * name = widget_get_string (widget);
969  if (name)
970  {
971  gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
972  g_free (name);
973  }
974 
975  g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, widget);
976 }
977 
978 static void create_entry (PreferencesWidget * widget, GtkWidget * * label,
979  GtkWidget * * entry, const char * domain)
980 {
981  *entry = gtk_entry_new();
982  gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password);
983 
984  if (widget->label)
985  * label = gtk_label_new (dgettext (domain, widget->label));
986 
987  if (widget->tooltip)
988  gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
989 
990  if (widget->cfg_type == VALUE_STRING)
991  {
992  char * value = widget_get_string (widget);
993  if (value)
994  {
995  gtk_entry_set_text ((GtkEntry *) * entry, value);
996  g_free (value);
997  }
998 
999  g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, widget);
1000  }
1001 }
1002 
1003 static void create_label (PreferencesWidget * widget, GtkWidget * * label,
1004  GtkWidget * * icon, const char * domain)
1005 {
1006  if (widget->data.label.stock_id)
1007  *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
1008 
1009  * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
1010  gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
1011 
1012  if (widget->data.label.single_line == FALSE)
1013  gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE);
1014 
1015  gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
1016 }
1017 
1018 static void create_cbox (PreferencesWidget * widget, GtkWidget * * label,
1019  GtkWidget * * combobox, const char * domain)
1020 {
1021  * combobox = gtk_combo_box_text_new ();
1022 
1023  if (widget->label) {
1024  * label = gtk_label_new (dgettext (domain, widget->label));
1025  }
1026 
1027  fill_cbox (* combobox, widget, domain);
1028 }
1029 
1030 static void fill_table (GtkWidget * table, PreferencesWidget * elements, int
1031  amt, const char * domain)
1032 {
1033  int x;
1034  GtkWidget *widget_left, *widget_middle, *widget_right;
1035  GtkAttachOptions middle_policy = (GtkAttachOptions) (0);
1036 
1037  for (x = 0; x < amt; ++x) {
1038  widget_left = widget_middle = widget_right = NULL;
1039  switch (elements[x].type) {
1040  case WIDGET_SPIN_BTN:
1041  create_spin_button (& elements[x], & widget_left,
1042  & widget_middle, & widget_right, domain);
1043  middle_policy = (GtkAttachOptions) (GTK_FILL);
1044  break;
1045  case WIDGET_LABEL:
1046  create_label (& elements[x], & widget_middle, & widget_left,
1047  domain);
1048  middle_policy = (GtkAttachOptions) (GTK_FILL);
1049  break;
1050  case WIDGET_FONT_BTN:
1051  create_font_btn (& elements[x], & widget_left, & widget_middle,
1052  domain);
1053  middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
1054  break;
1055  case WIDGET_ENTRY:
1056  create_entry (& elements[x], & widget_left, & widget_middle,
1057  domain);
1058  middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
1059  break;
1060  case WIDGET_COMBO_BOX:
1061  create_cbox (& elements[x], & widget_left, & widget_middle,
1062  domain);
1063  middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
1064  break;
1065  default:
1066  g_warning("Unsupported widget type %d in table", elements[x].type);
1067  }
1068 
1069  if (widget_left)
1070  gtk_table_attach(GTK_TABLE (table), widget_left, 0, 1, x, x+1,
1071  (GtkAttachOptions) (0),
1072  (GtkAttachOptions) (0), 0, 0);
1073 
1074  if (widget_middle)
1075  gtk_table_attach(GTK_TABLE(table), widget_middle, 1, widget_right ? 2 : 3, x, x+1,
1076  middle_policy,
1077  (GtkAttachOptions) (0), 4, 0);
1078 
1079  if (widget_right)
1080  gtk_table_attach(GTK_TABLE(table), widget_right, 2, 3, x, x+1,
1081  (GtkAttachOptions) (0),
1082  (GtkAttachOptions) (0), 0, 0);
1083  }
1084 }
1085 
1086 /* void create_widgets_with_domain (GtkBox * box, PreferencesWidget * widgets,
1087  int amt, const char * domain) */
1088 void create_widgets_with_domain (void * box, PreferencesWidget * widgets, int
1089  amt, const char * domain)
1090 {
1091  int x;
1092  GtkWidget *alignment = NULL, *widget = NULL;
1093  GtkWidget *child_box = NULL;
1094  GSList *radio_btn_group = NULL;
1095 
1096  for (x = 0; x < amt; ++x) {
1097  if (widget && widgets[x].child)
1098  {
1099  if (!child_box) {
1100  child_box = gtk_vbox_new(FALSE, 0);
1101  g_object_set_data(G_OBJECT(widget), "child", child_box);
1102  alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
1103  gtk_box_pack_start(box, alignment, FALSE, FALSE, 0);
1104  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
1105  gtk_container_add (GTK_CONTAINER (alignment), child_box);
1106 
1107  if (GTK_IS_TOGGLE_BUTTON (widget))
1108  gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget));
1109  }
1110  } else
1111  child_box = NULL;
1112 
1113  alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
1114  gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
1115  gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0);
1116 
1117  if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN)
1118  radio_btn_group = NULL;
1119 
1120  switch(widgets[x].type) {
1121  case WIDGET_CHK_BTN:
1122  widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label));
1123  init_toggle_button (widget, & widgets[x]);
1124  break;
1125  case WIDGET_LABEL:
1126  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 12, 0, 0, 0);
1127 
1128  GtkWidget *label = NULL, *icon = NULL;
1129  create_label (& widgets[x], & label, & icon, domain);
1130 
1131  if (icon == NULL)
1132  widget = label;
1133  else {
1134  widget = gtk_hbox_new(FALSE, 6);
1135  gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0);
1136  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
1137  }
1138  break;
1139  case WIDGET_RADIO_BTN:
1140  widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
1141  dgettext (domain, widgets[x].label));
1142  radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
1143  init_toggle_button (widget, & widgets[x]);
1144  break;
1145  case WIDGET_SPIN_BTN:
1146  widget = gtk_hbox_new(FALSE, 6);
1147 
1148  GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL;
1149  create_spin_button (& widgets[x], & label_pre, & spin_btn,
1150  & label_past, domain);
1151 
1152  if (label_pre)
1153  gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0);
1154  if (spin_btn)
1155  gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0);
1156  if (label_past)
1157  gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0);
1158 
1159  break;
1160  case WIDGET_CUSTOM: /* custom widget. --nenolod */
1161  if (widgets[x].data.populate)
1162  widget = widgets[x].data.populate();
1163  else
1164  widget = NULL;
1165 
1166  break;
1167  case WIDGET_FONT_BTN:
1168  widget = gtk_hbox_new(FALSE, 6);
1169 
1170  GtkWidget *font_btn = NULL;
1171  create_font_btn (& widgets[x], & label, & font_btn, domain);
1172 
1173  if (label)
1174  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
1175  if (font_btn)
1176  gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0);
1177  break;
1178  case WIDGET_TABLE:
1179  widget = gtk_table_new(widgets[x].data.table.rows, 3, FALSE);
1180  fill_table (widget, widgets[x].data.table.elem,
1181  widgets[x].data.table.rows, domain);
1182  gtk_table_set_row_spacings(GTK_TABLE(widget), 6);
1183  break;
1184  case WIDGET_ENTRY:
1185  widget = gtk_hbox_new(FALSE, 6);
1186 
1187  GtkWidget *entry = NULL;
1188  create_entry (& widgets[x], & label, & entry, domain);
1189 
1190  if (label)
1191  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
1192  if (entry)
1193  gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
1194  break;
1195  case WIDGET_COMBO_BOX:
1196  widget = gtk_hbox_new(FALSE, 6);
1197 
1198  GtkWidget *combo = NULL;
1199  create_cbox (& widgets[x], & label, & combo, domain);
1200 
1201  if (label)
1202  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
1203  if (combo)
1204  gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0);
1205  break;
1206  case WIDGET_BOX:
1207  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0);
1208 
1209  if (widgets[x].data.box.horizontal) {
1210  widget = gtk_hbox_new(FALSE, 0);
1211  } else {
1212  widget = gtk_vbox_new(FALSE, 0);
1213  }
1214 
1215  create_widgets_with_domain ((GtkBox *) widget,
1216  widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain);
1217 
1218  if (widgets[x].data.box.frame) {
1219  GtkWidget *tmp;
1220  tmp = widget;
1221 
1222  widget = gtk_frame_new (dgettext (domain, widgets[x].label));
1223  gtk_container_add(GTK_CONTAINER(widget), tmp);
1224  }
1225  break;
1226  case WIDGET_NOTEBOOK:
1227  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
1228 
1229  widget = gtk_notebook_new();
1230 
1231  int i;
1232  for (i = 0; i<widgets[x].data.notebook.n_tabs; i++) {
1233  GtkWidget *vbox;
1234  vbox = gtk_vbox_new(FALSE, 5);
1235  create_widgets_with_domain ((GtkBox *) vbox,
1236  widgets[x].data.notebook.tabs[i].settings,
1237  widgets[x].data.notebook.tabs[i].n_settings, domain);
1238 
1239  gtk_notebook_append_page (GTK_NOTEBOOK (widget), vbox,
1240  gtk_label_new (dgettext (domain,
1241  widgets[x].data.notebook.tabs[i].name)));
1242  }
1243  break;
1244  case WIDGET_SEPARATOR:
1245  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 0, 0);
1246 
1247  if (widgets[x].data.separator.horizontal == TRUE) {
1248  widget = gtk_hseparator_new();
1249  } else {
1250  widget = gtk_vseparator_new();
1251  }
1252  break;
1253  default:
1254  break;
1255  }
1256 
1257  if (widget && !gtk_widget_get_parent(widget))
1258  gtk_container_add(GTK_CONTAINER(alignment), widget);
1259  if (widget && widgets[x].tooltip && widgets[x].type != WIDGET_SPIN_BTN)
1260  gtk_widget_set_tooltip_text (widget, dgettext (domain,
1261  widgets[x].tooltip));
1262  }
1263 
1264 }
1265 
1266 static GtkWidget *
1268 {
1269  GtkWidget *titlestring_tag_menu, *menu_item;
1270  unsigned int i;
1271 
1272  titlestring_tag_menu = gtk_menu_new();
1273  for(i = 0; i < n_title_field_tags; i++) {
1274  menu_item = gtk_menu_item_new_with_label(_(title_field_tags[i].name));
1275  gtk_menu_shell_append(GTK_MENU_SHELL(titlestring_tag_menu), menu_item);
1276  g_signal_connect(menu_item, "activate",
1277  G_CALLBACK(titlestring_tag_menu_callback),
1278  GINT_TO_POINTER(i));
1279  };
1280  gtk_widget_show_all(titlestring_tag_menu);
1281 
1282  return titlestring_tag_menu;
1283 }
1284 
1285 static void show_numbers_cb (GtkToggleButton * numbers, void * unused)
1286 {
1287  set_bool (NULL, "show_numbers_in_pl", gtk_toggle_button_get_active (numbers));
1289  hook_call ("title change", NULL);
1290 }
1291 
1292 static void leading_zero_cb (GtkToggleButton * leading)
1293 {
1294  set_bool (NULL, "leading_zero", gtk_toggle_button_get_active (leading));
1296  hook_call ("title change", NULL);
1297 }
1298 
1299 static void create_titlestring_widgets (GtkWidget * * cbox, GtkWidget * * entry)
1300 {
1301  * cbox = gtk_combo_box_text_new ();
1302  for (int i = 0; i < TITLESTRING_NPRESETS; i ++)
1304  gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _("Custom"));
1305 
1306  * entry = gtk_entry_new ();
1307 
1308  char * format = get_string (NULL, "generic_title_format");
1309  update_titlestring_cbox ((GtkComboBox *) * cbox, format);
1310  gtk_entry_set_text ((GtkEntry *) * entry, format);
1311  g_free (format);
1312 
1313  g_signal_connect (* cbox, "changed", (GCallback) on_titlestring_cbox_changed, * entry);
1314  g_signal_connect (* entry, "changed", (GCallback) on_titlestring_entry_changed, * cbox);
1315 }
1316 
1317 static void
1319 {
1320  GtkWidget *vbox5;
1321  GtkWidget *alignment55;
1322  GtkWidget *label60;
1323  GtkWidget *alignment56;
1324  GtkWidget *table6;
1325  GtkWidget *titlestring_help_button;
1326  GtkWidget *image1;
1327  GtkWidget *label62;
1328  GtkWidget *label61;
1329  GtkWidget *alignment85;
1330  GtkWidget *label84;
1331  GtkWidget *alignment86;
1332  GtkWidget *hbox9;
1333  GtkWidget *vbox34;
1334  GtkWidget *image8;
1335  GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu();
1336  GtkWidget * numbers_alignment, * numbers;
1337 
1338  vbox5 = gtk_vbox_new (FALSE, 0);
1339  gtk_container_add ((GtkContainer *) category_notebook, vbox5);
1340 
1341  create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets));
1342 
1343  alignment55 = gtk_alignment_new (0.5, 0.5, 1, 1);
1344  gtk_box_pack_start (GTK_BOX (vbox5), alignment55, FALSE, FALSE, 0);
1345  gtk_alignment_set_padding ((GtkAlignment *) alignment55, 12, 3, 0, 0);
1346 
1347  label60 = gtk_label_new (_("<b>Song Display</b>"));
1348  gtk_container_add (GTK_CONTAINER (alignment55), label60);
1349  gtk_label_set_use_markup (GTK_LABEL (label60), TRUE);
1350  gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5);
1351 
1352  numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
1353  gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
1354  gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
1355 
1356  numbers = gtk_check_button_new_with_label (_("Show song numbers"));
1357  gtk_toggle_button_set_active ((GtkToggleButton *) numbers,
1358  get_bool (NULL, "show_numbers_in_pl"));
1359  g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
1360  show_numbers_cb, 0);
1361  gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
1362 
1363  numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
1364  gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
1365  gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
1366 
1367  numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 "
1368  "instead of 2:00)"));
1369  gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero"));
1370  g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
1371  leading_zero_cb, 0);
1372  gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
1373 
1374  alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1);
1375  gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0);
1376  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0);
1377 
1378  table6 = gtk_table_new (2, 3, FALSE);
1379  gtk_container_add (GTK_CONTAINER (alignment56), table6);
1380  gtk_table_set_row_spacings (GTK_TABLE (table6), 4);
1381  gtk_table_set_col_spacings (GTK_TABLE (table6), 12);
1382 
1383  titlestring_help_button = gtk_button_new ();
1384  gtk_table_attach (GTK_TABLE (table6), titlestring_help_button, 2, 3, 1, 2,
1385  (GtkAttachOptions) (0),
1386  (GtkAttachOptions) (0), 0, 0);
1387 
1388  gtk_widget_set_can_focus (titlestring_help_button, FALSE);
1389  gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format"));
1390  gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF);
1391  gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE);
1392 
1393  image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON);
1394  gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1);
1395 
1396  GtkWidget * titlestring_cbox;
1397  create_titlestring_widgets (& titlestring_cbox, & titlestring_entry);
1398  gtk_table_attach (GTK_TABLE (table6), titlestring_cbox, 1, 3, 0, 1,
1399  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1400  (GtkAttachOptions) (0), 0, 0);
1401  gtk_table_attach (GTK_TABLE (table6), titlestring_entry, 1, 2, 1, 2,
1402  (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1403  (GtkAttachOptions) (0), 0, 0);
1404 
1405  label62 = gtk_label_new (_("Custom string:"));
1406  gtk_table_attach (GTK_TABLE (table6), label62, 0, 1, 1, 2,
1407  (GtkAttachOptions) (0),
1408  (GtkAttachOptions) (0), 0, 0);
1409  gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT);
1410  gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5);
1411 
1412  label61 = gtk_label_new (_("Title format:"));
1413  gtk_table_attach (GTK_TABLE (table6), label61, 0, 1, 0, 1,
1414  (GtkAttachOptions) (0),
1415  (GtkAttachOptions) (0), 0, 0);
1416  gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT);
1417  gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5);
1418 
1419  alignment85 = gtk_alignment_new (0.5, 0.5, 1, 1);
1420  gtk_box_pack_start (GTK_BOX (vbox5), alignment85, FALSE, FALSE, 0);
1421  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment85), 12, 12, 0, 0);
1422 
1423  label84 = gtk_label_new (_("<b>Popup Information</b>"));
1424  gtk_container_add (GTK_CONTAINER (alignment85), label84);
1425  gtk_label_set_use_markup (GTK_LABEL (label84), TRUE);
1426  gtk_misc_set_alignment (GTK_MISC (label84), 0, 0.5);
1427 
1428  alignment86 = gtk_alignment_new (0.5, 0.5, 1, 1);
1429  gtk_box_pack_start (GTK_BOX (vbox5), alignment86, FALSE, FALSE, 0);
1430  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment86), 0, 0, 12, 0);
1431 
1432  hbox9 = gtk_hbox_new (FALSE, 12);
1433  gtk_container_add (GTK_CONTAINER (alignment86), hbox9);
1434 
1435  vbox34 = gtk_vbox_new (FALSE, 0);
1436  gtk_box_pack_start (GTK_BOX (hbox9), vbox34, TRUE, TRUE, 0);
1437 
1438  filepopupbutton = gtk_check_button_new_with_mnemonic (_("Show popup information for playlist entries"));
1439  gtk_widget_set_tooltip_text (filepopupbutton, _("Toggles popup information window for the pointed entry in the playlist. The window shows title of song, name of album, genre, year of publish, track number, track length, and artwork."));
1440  gtk_toggle_button_set_active ((GtkToggleButton *) filepopupbutton,
1441  get_bool (NULL, "show_filepopup_for_tuple"));
1442  gtk_box_pack_start ((GtkBox *) vbox34, filepopupbutton, TRUE, FALSE, 0);
1443 
1444  filepopup_settings_button = gtk_button_new ();
1445  gtk_widget_set_sensitive (filepopup_settings_button,
1446  get_bool (NULL, "show_filepopup_for_tuple"));
1447  gtk_box_pack_start (GTK_BOX (hbox9), filepopup_settings_button, FALSE, FALSE, 0);
1448 
1450  gtk_widget_set_tooltip_text (filepopup_settings_button, _("Edit settings for popup information"));
1451  gtk_button_set_relief (GTK_BUTTON (filepopup_settings_button), GTK_RELIEF_HALF);
1452 
1453  image8 = gtk_image_new_from_stock ("gtk-properties", GTK_ICON_SIZE_BUTTON);
1454  gtk_container_add (GTK_CONTAINER (filepopup_settings_button), image8);
1455 
1456 
1457 
1458  g_signal_connect (filepopupbutton, "toggled",
1459  G_CALLBACK(on_show_filepopup_toggled),
1460  NULL);
1461  g_signal_connect(G_OBJECT(filepopup_settings_button), "clicked",
1462  G_CALLBACK(on_filepopup_settings_clicked),
1463  NULL);
1464 
1465  g_signal_connect(titlestring_help_button, "clicked",
1467  titlestring_tag_menu);
1468 
1469  /* Create window for filepopup settings */
1471 }
1472 
1474 
1475 static bool_t output_enum_cb (PluginHandle * plugin, GList * * list)
1476 {
1477  * list = g_list_prepend (* list, plugin);
1478  return TRUE;
1479 }
1480 
1481 static GList * output_get_list (void)
1482 {
1483  static GList * list = NULL;
1484 
1485  if (list == NULL)
1486  {
1488  & list);
1489  list = g_list_reverse (list);
1490  }
1491 
1492  return list;
1493 }
1494 
1495 static void output_combo_update (GtkComboBox * combo)
1496 {
1498  gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin));
1499  gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
1500  gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
1501 }
1502 
1503 static void output_combo_changed (GtkComboBox * combo)
1504 {
1505  PluginHandle * plugin = g_list_nth_data (output_get_list (),
1506  gtk_combo_box_get_active (combo));
1507  g_return_if_fail (plugin != NULL);
1508 
1509  plugin_enable (plugin, TRUE);
1510  output_combo_update (combo);
1511 }
1512 
1513 static void output_combo_fill (GtkComboBox * combo)
1514 {
1515  for (GList * node = output_get_list (); node != NULL; node = node->next)
1517  plugin_get_name (node->data));
1518 }
1519 
1520 static void output_do_config (void)
1521 {
1522  OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
1523  g_return_if_fail (op != NULL);
1524  if (op->configure != NULL)
1525  op->configure ();
1526  else if (op->settings != NULL)
1527  plugin_preferences_show (op->settings);
1528 }
1529 
1530 static void output_do_about (void)
1531 {
1532  OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
1533  g_return_if_fail (op != NULL);
1534  if (op->about != NULL)
1535  op->about ();
1536 }
1537 
1538 static void * create_output_plugin_box (void)
1539 {
1540  GtkWidget * hbox1 = gtk_hbox_new (FALSE, 6);
1541  gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0);
1542 
1543  GtkWidget * vbox = gtk_vbox_new (FALSE, 6);
1544  gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0);
1545 
1546  GtkWidget * hbox2 = gtk_hbox_new (FALSE, 6);
1547  gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0);
1548 
1549  GtkWidget * output_plugin_cbox = gtk_combo_box_text_new ();
1550  gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0);
1551 
1552  GtkWidget * hbox3 = gtk_hbox_new (FALSE, 6);
1553  gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0);
1554 
1555  output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
1556  gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0);
1557 
1558  output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
1559  gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0);
1560 
1561  output_combo_fill ((GtkComboBox *) output_plugin_cbox);
1562  output_combo_update ((GtkComboBox *) output_plugin_cbox);
1563 
1564  g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL);
1565  g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL);
1566  g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL);
1567 
1568  return hbox1;
1569 }
1570 
1571 static void create_audio_category (void)
1572 {
1573  GtkWidget * audio_page_vbox = gtk_vbox_new (FALSE, 0);
1574  create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets));
1575  gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox);
1576 }
1577 
1578 static void
1580 {
1581  GtkWidget *connectivity_page_vbox;
1582  GtkWidget *vbox29;
1583 
1584  connectivity_page_vbox = gtk_vbox_new (FALSE, 0);
1585  gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox);
1586 
1587  vbox29 = gtk_vbox_new (FALSE, 0);
1588  gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0);
1589 
1590  create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets));
1591 }
1592 
1593 static void create_plugin_category (void)
1594 {
1595  GtkWidget * notebook = gtk_notebook_new ();
1596  gtk_container_add ((GtkContainer *) category_notebook, notebook);
1597 
1600  const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"),
1601  N_("Effect"), N_("Visualization"), N_("General")};
1602 
1603  for (int i = 0; i < G_N_ELEMENTS (types); i ++)
1604  gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new
1605  (types[i]), gtk_label_new (_(names[i])));
1606 }
1607 
1608 static bool_t
1609 prefswin_destroy(GtkWidget *window, GdkEvent *event, gpointer data)
1610 {
1611  prefswin = NULL;
1613  gtk_widget_destroy(filepopup_settings);
1615  gtk_widget_destroy(window);
1616  return TRUE;
1617 }
1618 
1619 /* GtkWidget * * create_prefs_window (void) */
1620 void * * create_prefs_window (void)
1621 {
1622  char *aud_version_string;
1623 
1624  GtkWidget *vbox;
1625  GtkWidget *hbox1;
1626  GtkWidget *scrolledwindow6;
1627  GtkWidget *hseparator1;
1628  GtkWidget *hbox4;
1629  GtkWidget *audversionlabel;
1630  GtkWidget *prefswin_button_box;
1631  GtkWidget *hbox11;
1632  GtkWidget *image10;
1633  GtkWidget *close;
1634  GtkAccelGroup *accel_group;
1635 
1636  accel_group = gtk_accel_group_new ();
1637 
1638  prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1639  gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG);
1640  gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12);
1641  gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences"));
1642  gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER);
1643  gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400);
1644 
1645  vbox = gtk_vbox_new (FALSE, 0);
1646  gtk_container_add (GTK_CONTAINER (prefswin), vbox);
1647 
1648  hbox1 = gtk_hbox_new (FALSE, 8);
1649  gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);
1650 
1651  scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL);
1652  gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0);
1653  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1654  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN);
1655 
1656  category_treeview = gtk_tree_view_new ();
1657  gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview);
1658  gtk_widget_set_size_request (scrolledwindow6, 168, -1);
1659  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE);
1660 
1661  category_notebook = gtk_notebook_new ();
1662  gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0);
1663 
1665  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE);
1666  gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE);
1667  gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE);
1668 
1673 
1674  hseparator1 = gtk_hseparator_new ();
1675  gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6);
1676 
1677  hbox4 = gtk_hbox_new (FALSE, 0);
1678  gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0);
1679 
1680  audversionlabel = gtk_label_new ("");
1681  gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0);
1682  gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE);
1683 
1684  prefswin_button_box = gtk_hbutton_box_new ();
1685  gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0);
1686  gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END);
1687  gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6);
1688 
1689  hbox11 = gtk_hbox_new (FALSE, 2);
1690 
1691  image10 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
1692  gtk_box_pack_start (GTK_BOX (hbox11), image10, FALSE, FALSE, 0);
1693 
1694  close = gtk_button_new_from_stock ("gtk-close");
1695  gtk_container_add (GTK_CONTAINER (prefswin_button_box), close);
1697  gtk_widget_add_accelerator (close, "clicked", accel_group,
1698  GDK_Escape, (GdkModifierType) 0,
1699  GTK_ACCEL_VISIBLE);
1700 
1701 
1702  gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group);
1703 
1704  /* connect signals */
1705  g_signal_connect(G_OBJECT(prefswin), "delete_event",
1706  G_CALLBACK(prefswin_destroy),
1707  NULL);
1708  g_signal_connect_swapped(G_OBJECT(close), "clicked",
1709  G_CALLBACK(prefswin_destroy),
1710  prefswin);
1711 
1712  /* create category view */
1713  fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook);
1714 
1715  /* audacious version label */
1716 
1717  aud_version_string = g_strdup_printf
1718  ("<span size='small'>%s (%s)</span>", "Audacious " VERSION, BUILDSTAMP);
1719 
1720  gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string );
1721  g_free(aud_version_string);
1722  gtk_widget_show_all(vbox);
1723 
1724  return & prefswin;
1725 }
1726 
1727 void
1729 {
1731 }
1732 
1734 {
1735  if (! prefswin)
1737 
1738  gtk_window_present ((GtkWindow *) prefswin);
1739 }
1740 
1741 void
1743 {
1744  g_return_if_fail(prefswin);
1745  gtk_widget_hide(GTK_WIDGET(prefswin));
1746 }
1747 
1748 static void prefswin_page_queue_new (GtkWidget * container, const char * name,
1749  const char * imgurl)
1750 {
1751  CategoryQueueEntry *ent = g_new0(CategoryQueueEntry, 1);
1752 
1753  ent->container = container;
1754  ent->pg_name = name;
1755  ent->img_url = imgurl;
1756 
1757  if (category_queue)
1758  ent->next = category_queue;
1759 
1760  category_queue = ent;
1761 }
1762 
1763 static void
1765 {
1766  category_queue = ent->next;
1767  g_free(ent);
1768 }
1769 
1770 /*
1771  * Public APIs for adding new pages to the prefs window.
1772  *
1773  * Basically, the concept here is that third party components can register themselves in the root
1774  * preferences window.
1775  *
1776  * From a usability standpoint this makes the application look more "united", instead of cluttered
1777  * and malorganised. Hopefully this option will be used further in the future.
1778  *
1779  * - nenolod
1780  */
1781 /* int prefswin_page_new (GtkWidget * container, const char * name,
1782  const char * imgurl) */
1783 int prefswin_page_new (void * container, const char * name, const char *
1784  imgurl)
1785 {
1786  GtkTreeModel *model;
1787  GtkTreeIter iter;
1788  GdkPixbuf *img = NULL;
1789  GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
1790  int id;
1791 
1792  if (treeview == NULL || category_notebook == NULL)
1793  {
1794  prefswin_page_queue_new(container, name, imgurl);
1795  return -1;
1796  }
1797 
1798  model = gtk_tree_view_get_model(treeview);
1799 
1800  if (model == NULL)
1801  {
1802  prefswin_page_queue_new(container, name, imgurl);
1803  return -1;
1804  }
1805 
1806  /* Make sure the widgets are visible. */
1807  gtk_widget_show(container);
1808  id = gtk_notebook_append_page(GTK_NOTEBOOK(category_notebook), container, NULL);
1809 
1810  if (id == -1)
1811  return -1;
1812 
1813  if (imgurl != NULL)
1814  img = gdk_pixbuf_new_from_file(imgurl, NULL);
1815 
1816  gtk_list_store_append(GTK_LIST_STORE(model), &iter);
1817  gtk_list_store_set(GTK_LIST_STORE(model), &iter,
1820  name, CATEGORY_VIEW_COL_ID, id, -1);
1821 
1822  if (img != NULL)
1823  g_object_unref(img);
1824 
1825  return id;
1826 }
1827 
1828 void
1829 prefswin_page_destroy(GtkWidget *container)
1830 {
1831  GtkTreeModel *model;
1832  GtkTreeIter iter;
1833  GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
1834  bool_t ret;
1835  int id;
1836  int index = -1;
1837 
1838  if (category_notebook == NULL || treeview == NULL || container == NULL)
1839  return;
1840 
1841  id = gtk_notebook_page_num(GTK_NOTEBOOK(category_notebook), container);
1842 
1843  if (id == -1)
1844  return;
1845 
1846  gtk_notebook_remove_page(GTK_NOTEBOOK(category_notebook), id);
1847 
1848  model = gtk_tree_view_get_model(treeview);
1849 
1850  if (model == NULL)
1851  return;
1852 
1853  ret = gtk_tree_model_get_iter_first(model, &iter);
1854 
1855  while (ret == TRUE)
1856  {
1857  gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
1858 
1859  if (index == id)
1860  {
1861  gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
1862  ret = gtk_tree_model_get_iter_first(model, &iter);
1863  continue;
1864  }
1865 
1866  if (index > id)
1867  {
1868  index--;
1869  gtk_list_store_set(GTK_LIST_STORE(model), &iter, CATEGORY_VIEW_COL_ID, index, -1);
1870  }
1871 
1872  ret = gtk_tree_model_iter_next(model, &iter);
1873  }
1874 }
1875 
1876 static void sw_volume_toggled (void)
1877 {
1878  int vol[2];
1879 
1880  if (get_bool (NULL, "software_volume_control"))
1881  {
1882  vol[0] = get_int (NULL, "sw_volume_left");
1883  vol[1] = get_int (NULL, "sw_volume_right");
1884  }
1885  else
1886  playback_get_volume (& vol[0], & vol[1]);
1887 
1888  hook_call ("volume set", vol);
1889 }