Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
ui_preferences.c
Go to the documentation of this file.
1 /*
2  * ui_preferences.c
3  * Copyright 2006-2011 William Pitcock, Tomasz Moń, Michael Färber, and
4  * John Lindgren
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions, and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions, and the following disclaimer in the documentation
14  * provided with the distribution.
15  *
16  * This software is provided "as is" and without any warranty, express or
17  * implied. In no event shall the authors be liable for any damages arising from
18  * the use of this software.
19  */
20 
21 #include <string.h>
22 
23 #include <gdk/gdkkeysyms.h>
24 #include <gtk/gtk.h>
25 
26 #include <libaudcore/hook.h>
27 
28 #include "debug.h"
29 #include "i18n.h"
30 #include "misc.h"
31 #include "output.h"
32 #include "playlist.h"
33 #include "plugin.h"
34 #include "plugins.h"
35 #include "preferences.h"
36 #include "ui_preferences.h"
37 
38 #ifdef USE_CHARDET
39 #include <libguess.h>
40 #endif
41 
47 };
48 
49 typedef struct {
50  const char *icon_path;
51  const char *name;
52 } Category;
53 
54 typedef struct {
55  const char *name;
56  const char *tag;
58 
59 static /* GtkWidget * */ void * prefswin = NULL;
60 static GtkWidget *category_treeview = NULL;
61 static GtkWidget *category_notebook = NULL;
62 
63 /* prefswin widgets */
64 GtkWidget *titlestring_entry;
65 
66 static Category categories[] = {
67  {"audio.png", N_("Audio")},
68  {"connectivity.png", N_("Network")},
69  {"playlist.png", N_("Playlist")},
70  {"info.png", N_("Song Info")},
71  {"plugins.png", N_("Plugins")},
72 };
73 
74 static int n_categories = G_N_ELEMENTS(categories);
75 
77  { N_("Artist") , "${artist}" },
78  { N_("Album") , "${album}" },
79  { N_("Title") , "${title}" },
80  { N_("Tracknumber"), "${track-number}" },
81  { N_("Genre") , "${genre}" },
82  { N_("Filename") , "${file-name}" },
83  { N_("Filepath") , "${file-path}" },
84  { N_("Date") , "${date}" },
85  { N_("Year") , "${year}" },
86  { N_("Comment") , "${comment}" },
87  { N_("Codec") , "${codec}" },
88  { N_("Quality") , "${quality}" },
89 };
90 static const unsigned int n_title_field_tags = G_N_ELEMENTS(title_field_tags);
91 
92 #ifdef USE_CHARDET
93 static ComboBoxElements chardet_detector_presets[] = {
94  {"", N_("None")},
95  {GUESS_REGION_AR, N_("Arabic")},
96  {GUESS_REGION_BL, N_("Baltic")},
97  {GUESS_REGION_CN, N_("Chinese")},
98  {GUESS_REGION_GR, N_("Greek")},
99  {GUESS_REGION_HW, N_("Hebrew")},
100  {GUESS_REGION_JP, N_("Japanese")},
101  {GUESS_REGION_KR, N_("Korean")},
102  {GUESS_REGION_PL, N_("Polish")},
103  {GUESS_REGION_RU, N_("Russian")},
104  {GUESS_REGION_TW, N_("Taiwanese")},
105  {GUESS_REGION_TR, N_("Turkish")}};
106 #endif
107 
109  { GINT_TO_POINTER(16), "16" },
110  { GINT_TO_POINTER(24), "24" },
111  { GINT_TO_POINTER(32), "32" },
112  {GINT_TO_POINTER (0), N_("Floating point")},
113 };
114 
115 static void * create_output_plugin_box (void);
116 static void output_bit_depth_changed (void);
117 
118 static PreferencesWidget rg_mode_widgets[] = {
119  {WIDGET_CHK_BTN, N_("Album mode"), .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}};
120 
121 static PreferencesWidget audio_page_widgets[] = {
122  {WIDGET_LABEL, N_("<b>Output Settings</b>")},
123  {WIDGET_CUSTOM, .data = {.populate = create_output_plugin_box}},
124  {WIDGET_COMBO_BOX, N_("Bit depth:"),
125  .cfg_type = VALUE_INT, .cname = "output_bit_depth", .callback = output_bit_depth_changed,
126  .data = {.combo = {bitdepth_elements, G_N_ELEMENTS (bitdepth_elements)}}},
127  {WIDGET_SPIN_BTN, N_("Buffer size:"),
128  .cfg_type = VALUE_INT, .cname = "output_buffer_size",
129  .data = {.spin_btn = {100, 10000, 1000, N_("ms")}}},
130  {WIDGET_CHK_BTN, N_("Soft clipping"),
131  .cfg_type = VALUE_BOOLEAN, .cname = "soft_clipping"},
132  {WIDGET_CHK_BTN, N_("Use software volume control (not recommended)"),
133  .cfg_type = VALUE_BOOLEAN, .cname = "software_volume_control"},
134  {WIDGET_LABEL, N_("<b>Replay Gain</b>")},
135  {WIDGET_CHK_BTN, N_("Enable Replay Gain"),
136  .cfg_type = VALUE_BOOLEAN, .cname = "enable_replay_gain"},
137  {WIDGET_BOX, .child = TRUE, .data = {.box = {rg_mode_widgets, G_N_ELEMENTS (rg_mode_widgets), TRUE}}},
138  {WIDGET_CHK_BTN, N_("Prevent clipping (recommended)"), .child = TRUE,
139  .cfg_type = VALUE_BOOLEAN, .cname = "enable_clipping_prevention"},
140  {WIDGET_LABEL, N_("<b>Adjust Levels</b>"), .child = TRUE},
141  {WIDGET_SPIN_BTN, N_("Amplify all files:"), .child = TRUE,
142  .cfg_type = VALUE_FLOAT, .cname = "replay_gain_preamp",
143  .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}},
144  {WIDGET_SPIN_BTN, N_("Amplify untagged files:"), .child = TRUE,
145  .cfg_type = VALUE_FLOAT, .cname = "default_gain",
146  .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}};
147 
148 static PreferencesWidget proxy_host_port_elements[] = {
149  {WIDGET_ENTRY, N_("Proxy hostname:"), .cfg_type = VALUE_STRING, .cname = "proxy_host"},
150  {WIDGET_ENTRY, N_("Proxy port:"), .cfg_type = VALUE_STRING, .cname = "proxy_port"}};
151 
152 static PreferencesWidget proxy_auth_elements[] = {
153  {WIDGET_ENTRY, N_("Proxy username:"), .cfg_type = VALUE_STRING, .cname = "proxy_user"},
154  {WIDGET_ENTRY, N_("Proxy password:"), .cfg_type = VALUE_STRING, .cname = "proxy_pass",
155  .data = {.entry = {.password = TRUE}}}};
156 
157 static PreferencesWidget connectivity_page_widgets[] = {
158  {WIDGET_LABEL, N_("<b>Proxy Configuration</b>"), NULL, NULL, NULL, FALSE},
159  {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"},
160  {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_host_port_elements,
161  G_N_ELEMENTS (proxy_host_port_elements)}}},
162  {WIDGET_CHK_BTN, N_("Use authentication with proxy"),
163  .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"},
164  {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_auth_elements,
165  G_N_ELEMENTS (proxy_auth_elements)}}}
166 };
167 
168 static PreferencesWidget chardet_elements[] = {
169 #ifdef USE_CHARDET
170  {WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"),
171  .cfg_type = VALUE_STRING, .cname = "chardet_detector", .child = TRUE,
172  .data = {.combo = {chardet_detector_presets, G_N_ELEMENTS (chardet_detector_presets)}}},
173 #endif
174  {WIDGET_ENTRY, N_("Fallback character encodings:"), .cfg_type = VALUE_STRING,
175  .cname = "chardet_fallback", .child = TRUE}};
176 
177 static PreferencesWidget playlist_page_widgets[] = {
178  {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE},
179  {WIDGET_CHK_BTN, N_("Continue playback on startup"),
180  .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"},
181  {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"),
182  .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"},
183  {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"),
184  .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"},
185  {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"),
186  .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"},
187  {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"),
188  .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play",
189  .callback = playlist_trigger_scan},
190  {WIDGET_LABEL, N_("<b>Compatibility</b>"), NULL, NULL, NULL, FALSE},
191  {WIDGET_CHK_BTN, N_("Interpret \\ (backward slash) as a folder delimiter"),
192  .cfg_type = VALUE_BOOLEAN, .cname = "convert_backslash"},
193  {WIDGET_TABLE, .data = {.table = {chardet_elements,
194  G_N_ELEMENTS (chardet_elements)}}}
195 };
196 
197 static PreferencesWidget song_info_page_widgets[] = {
198  {WIDGET_LABEL, N_("<b>Album Art</b>")},
199  {WIDGET_LABEL, N_("Search for images matching these words (comma-separated):")},
200  {WIDGET_ENTRY, .cfg_type = VALUE_STRING, .cname = "cover_name_include"},
201  {WIDGET_LABEL, N_("Exclude images matching these words (comma-separated):")},
202  {WIDGET_ENTRY, .cfg_type = VALUE_STRING, .cname = "cover_name_exclude"},
203  {WIDGET_CHK_BTN, N_("Search for images matching song file name"),
204  .cfg_type = VALUE_BOOLEAN, .cname = "use_file_cover"},
205  {WIDGET_CHK_BTN, N_("Search recursively"),
206  .cfg_type = VALUE_BOOLEAN, .cname = "recurse_for_cover"},
207  {WIDGET_SPIN_BTN, N_("Search depth:"), .child = TRUE,
208  .cfg_type = VALUE_INT, .cname = "recurse_for_cover_depth",
209  .data = {.spin_btn = {0, 100, 1}}},
210  {WIDGET_LABEL, N_("<b>Popup Information</b>")},
211  {WIDGET_CHK_BTN, N_("Show popup information"),
212  .cfg_type = VALUE_BOOLEAN, .cname = "show_filepopup_for_tuple"},
213  {WIDGET_SPIN_BTN, N_("Popup delay (tenths of a second):"), .child = TRUE,
214  .cfg_type = VALUE_INT, .cname = "filepopup_delay",
215  .data = {.spin_btn = {0, 100, 1}}},
216  {WIDGET_CHK_BTN, N_("Show time scale for current song"), .child = TRUE,
217  .cfg_type = VALUE_BOOLEAN, .cname = "filepopup_showprogressbar"}};
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 
237 static void
238 change_category(GtkNotebook * notebook,
239  GtkTreeSelection * selection)
240 {
241  GtkTreeModel *model;
242  GtkTreeIter iter;
243  int index;
244 
245  if (!gtk_tree_selection_get_selected(selection, &model, &iter))
246  return;
247 
248  gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
249  gtk_notebook_set_current_page(notebook, index);
250 }
251 
252 static void
253 titlestring_tag_menu_callback(GtkMenuItem * menuitem,
254  void * data)
255 {
256  const char *separator = " - ";
257  int item = GPOINTER_TO_INT(data);
258  int pos;
259 
260  pos = gtk_editable_get_position(GTK_EDITABLE(titlestring_entry));
261 
262  /* insert separator as needed */
263  if (gtk_entry_get_text(GTK_ENTRY(titlestring_entry))[0])
264  gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), separator, -1, &pos);
265 
266  gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), _(title_field_tags[item].tag), -1, &pos);
267  gtk_editable_set_position(GTK_EDITABLE(titlestring_entry), pos);
268 }
269 
270 static void
272  void * data)
273 {
274  GtkMenu * menu = data;
275  gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
276 }
277 
278 static void update_titlestring_cbox (GtkComboBox * cbox, const char * format)
279 {
280  int preset;
281  for (preset = 0; preset < TITLESTRING_NPRESETS; preset ++)
282  {
283  if (! strcmp (titlestring_presets[preset], format))
284  break;
285  }
286 
287  if (gtk_combo_box_get_active (cbox) != preset)
288  gtk_combo_box_set_active (cbox, preset);
289 }
290 
291 static void on_titlestring_entry_changed (GtkEntry * entry, GtkComboBox * cbox)
292 {
293  const char * format = gtk_entry_get_text (entry);
294  set_string (NULL, "generic_title_format", format);
295  update_titlestring_cbox (cbox, format);
297 }
298 
299 static void on_titlestring_cbox_changed (GtkComboBox * cbox, GtkEntry * entry)
300 {
301  int preset = gtk_combo_box_get_active (cbox);
302  if (preset < TITLESTRING_NPRESETS)
303  gtk_entry_set_text (entry, titlestring_presets[preset]);
304 }
305 
306 static void widget_set_bool (const PreferencesWidget * widget, bool_t value)
307 {
308  g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN);
309 
310  if (widget->cfg)
311  * (bool_t *) widget->cfg = value;
312  else if (widget->cname)
313  set_bool (widget->csect, widget->cname, value);
314 
315  if (widget->callback)
316  widget->callback ();
317 }
318 
319 static bool_t widget_get_bool (const PreferencesWidget * widget)
320 {
321  g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE);
322 
323  if (widget->cfg)
324  return * (bool_t *) widget->cfg;
325  else if (widget->cname)
326  return get_bool (widget->csect, widget->cname);
327  else
328  return FALSE;
329 }
330 
331 static void widget_set_int (const PreferencesWidget * widget, int value)
332 {
333  g_return_if_fail (widget->cfg_type == VALUE_INT);
334 
335  if (widget->cfg)
336  * (int *) widget->cfg = value;
337  else if (widget->cname)
338  set_int (widget->csect, widget->cname, value);
339 
340  if (widget->callback)
341  widget->callback ();
342 }
343 
344 static int widget_get_int (const PreferencesWidget * widget)
345 {
346  g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0);
347 
348  if (widget->cfg)
349  return * (int *) widget->cfg;
350  else if (widget->cname)
351  return get_int (widget->csect, widget->cname);
352  else
353  return 0;
354 }
355 
356 static void widget_set_double (const PreferencesWidget * widget, double value)
357 {
358  g_return_if_fail (widget->cfg_type == VALUE_FLOAT);
359 
360  if (widget->cfg)
361  * (float *) widget->cfg = value;
362  else if (widget->cname)
363  set_double (widget->csect, widget->cname, value);
364 
365  if (widget->callback)
366  widget->callback ();
367 }
368 
369 static double widget_get_double (const PreferencesWidget * widget)
370 {
371  g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0);
372 
373  if (widget->cfg)
374  return * (float *) widget->cfg;
375  else if (widget->cname)
376  return get_double (widget->csect, widget->cname);
377  else
378  return 0;
379 }
380 
381 static void widget_set_string (const PreferencesWidget * widget, const char * value)
382 {
383  g_return_if_fail (widget->cfg_type == VALUE_STRING);
384 
385  if (widget->cfg)
386  {
387  g_free (* (char * *) widget->cfg);
388  * (char * *) widget->cfg = g_strdup (value);
389  }
390  else if (widget->cname)
391  set_string (widget->csect, widget->cname, value);
392 
393  if (widget->callback)
394  widget->callback ();
395 }
396 
397 static char * widget_get_string (const PreferencesWidget * widget)
398 {
399  g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL);
400 
401  if (widget->cfg)
402  return g_strdup (* (char * *) widget->cfg);
403  else if (widget->cname)
404  return get_string (widget->csect, widget->cname);
405  else
406  return NULL;
407 }
408 
409 static void on_font_btn_font_set (GtkFontButton * button, const PreferencesWidget * widget)
410 {
411  widget_set_string (widget, gtk_font_button_get_font_name (button));
412 }
413 
414 static void on_spin_btn_changed_int (GtkSpinButton * button, const PreferencesWidget * widget)
415 {
416  widget_set_int (widget, gtk_spin_button_get_value_as_int (button));
417 }
418 
419 static void on_spin_btn_changed_float (GtkSpinButton * button, const PreferencesWidget * widget)
420 {
421  widget_set_double (widget, gtk_spin_button_get_value (button));
422 }
423 
424 static void fill_category_list (GtkTreeView * treeview, GtkNotebook * notebook)
425 {
426  GtkListStore *store;
427  GtkCellRenderer *renderer;
428  GtkTreeViewColumn *column;
429  GtkTreeSelection *selection;
430  GtkTreeIter iter;
431  GdkPixbuf *img;
432  int i;
433 
434  column = gtk_tree_view_column_new();
435  gtk_tree_view_column_set_title(column, _("Category"));
436  gtk_tree_view_append_column(treeview, column);
437  gtk_tree_view_column_set_spacing(column, 2);
438 
439  renderer = gtk_cell_renderer_pixbuf_new();
440  gtk_tree_view_column_pack_start(column, renderer, FALSE);
441  gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", 0, NULL);
442 
443  renderer = gtk_cell_renderer_text_new();
444  gtk_tree_view_column_pack_start(column, renderer, FALSE);
445  gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
446 
447  g_object_set ((GObject *) renderer, "wrap-width", 96, "wrap-mode",
448  PANGO_WRAP_WORD_CHAR, NULL);
449 
450  store = gtk_list_store_new(CATEGORY_VIEW_N_COLS,
451  GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
452  gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
453 
454  for (i = 0; i < n_categories; i ++)
455  {
456  char * path = g_strdup_printf ("%s/images/%s",
457  get_path (AUD_PATH_DATA_DIR), categories[i].icon_path);
458  img = gdk_pixbuf_new_from_file (path, NULL);
459  g_free (path);
460 
461  gtk_list_store_append(store, &iter);
462  gtk_list_store_set(store, &iter,
465  gettext(categories[i].name), CATEGORY_VIEW_COL_ID,
466  i, -1);
467  g_object_unref(img);
468  }
469 
470  selection = gtk_tree_view_get_selection(treeview);
471 
472  g_signal_connect_swapped(selection, "changed",
473  G_CALLBACK(change_category), notebook);
474 }
475 
476 static void on_radio_button_toggled (GtkWidget * button, const PreferencesWidget * widget)
477 {
478  if (gtk_toggle_button_get_active ((GtkToggleButton *) button))
479  widget_set_int (widget, widget->data.radio_btn.value);
480 }
481 
482 static void init_radio_button (GtkWidget * button, const PreferencesWidget * widget)
483 {
484  if (widget->cfg_type != VALUE_INT)
485  return;
486 
487  if (widget_get_int (widget) == widget->data.radio_btn.value)
488  gtk_toggle_button_set_active ((GtkToggleButton *) button, TRUE);
489 
490  g_signal_connect (button, "toggled", (GCallback) on_radio_button_toggled, (void *) widget);
491 }
492 
493 static void on_toggle_button_toggled (GtkToggleButton * button, const PreferencesWidget * widget)
494 {
495  bool_t active = gtk_toggle_button_get_active (button);
496  widget_set_bool (widget, active);
497 
498  GtkWidget * child = g_object_get_data ((GObject *) button, "child");
499  if (child)
500  gtk_widget_set_sensitive (child, active);
501 }
502 
503 static void init_toggle_button (GtkWidget * button, const PreferencesWidget * widget)
504 {
505  if (widget->cfg_type != VALUE_BOOLEAN)
506  return;
507 
508  gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget));
509  g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, (void *) widget);
510 }
511 
512 static void on_entry_changed (GtkEntry * entry, const PreferencesWidget * widget)
513 {
514  widget_set_string (widget, gtk_entry_get_text (entry));
515 }
516 
517 static void on_cbox_changed_int (GtkComboBox * combobox, const PreferencesWidget * widget)
518 {
519  int position = gtk_combo_box_get_active (combobox);
520  widget_set_int (widget, GPOINTER_TO_INT (widget->data.combo.elements[position].value));
521 }
522 
523 static void on_cbox_changed_string (GtkComboBox * combobox, const PreferencesWidget * widget)
524 {
525  int position = gtk_combo_box_get_active (combobox);
526  widget_set_string (widget, widget->data.combo.elements[position].value);
527 }
528 
529 static void fill_cbox (GtkWidget * combobox, const PreferencesWidget * widget, const char * domain)
530 {
531  for (int i = 0; i < widget->data.combo.n_elements; i ++)
532  gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox,
533  dgettext (domain, widget->data.combo.elements[i].label));
534 
535  switch (widget->cfg_type)
536  {
537  case VALUE_INT:;
538  int ivalue = widget_get_int (widget);
539 
540  for (int i = 0; i < widget->data.combo.n_elements; i++)
541  {
542  if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue)
543  {
544  gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
545  break;
546  }
547  }
548 
549  g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget);
550  break;
551 
552  case VALUE_STRING:;
553  char * value = widget_get_string (widget);
554 
555  for(int i = 0; i < widget->data.combo.n_elements; i++)
556  {
557  if (value && ! strcmp (widget->data.combo.elements[i].value, value))
558  {
559  gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
560  break;
561  }
562  }
563 
564  g_free (value);
565 
566  g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget);
567  break;
568 
569  default:
570  break;
571  }
572 }
573 
574 static void create_spin_button (const PreferencesWidget * widget, GtkWidget * *
575  label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char *
576  domain)
577 {
578  g_return_if_fail(widget->type == WIDGET_SPIN_BTN);
579 
580  * label_pre = gtk_label_new (dgettext (domain, widget->label));
581 
582  *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min,
583  widget->data.spin_btn.max,
584  widget->data.spin_btn.step);
585 
586 
587  if (widget->tooltip)
588  gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain,
589  widget->tooltip));
590 
591  if (widget->data.spin_btn.right_label) {
592  * label_past = gtk_label_new (dgettext (domain,
593  widget->data.spin_btn.right_label));
594  }
595 
596  switch (widget->cfg_type)
597  {
598  case VALUE_INT:
599  gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
600  g_signal_connect (* spin_btn, "value_changed", (GCallback)
601  on_spin_btn_changed_int, (void *) widget);
602  break;
603  case VALUE_FLOAT:
604  gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
605  g_signal_connect (* spin_btn, "value_changed", (GCallback)
606  on_spin_btn_changed_float, (void *) widget);
607  break;
608  default:
609  break;
610  }
611 }
612 
613 void create_font_btn (const PreferencesWidget * widget, GtkWidget * * label,
614  GtkWidget * * font_btn, const char * domain)
615 {
616  *font_btn = gtk_font_button_new();
617  gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE);
618  gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE);
619  gtk_widget_set_hexpand(*font_btn, TRUE);
620  if (widget->label) {
621  * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
622  gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
623  gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5);
624  gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
625  gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn);
626  }
627 
628  if (widget->data.font_btn.title)
629  gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn),
630  dgettext (domain, widget->data.font_btn.title));
631 
632  char * name = widget_get_string (widget);
633  if (name)
634  {
635  gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
636  g_free (name);
637  }
638 
639  g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, (void *) widget);
640 }
641 
642 static void create_entry (const PreferencesWidget * widget, GtkWidget * * label,
643  GtkWidget * * entry, const char * domain)
644 {
645  *entry = gtk_entry_new();
646  gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password);
647  gtk_widget_set_hexpand(*entry, TRUE);
648 
649  if (widget->label)
650  * label = gtk_label_new (dgettext (domain, widget->label));
651 
652  if (widget->tooltip)
653  gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
654 
655  if (widget->cfg_type == VALUE_STRING)
656  {
657  char * value = widget_get_string (widget);
658  if (value)
659  {
660  gtk_entry_set_text ((GtkEntry *) * entry, value);
661  g_free (value);
662  }
663 
664  g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, (void *) widget);
665  }
666 }
667 
668 static void create_label (const PreferencesWidget * widget, GtkWidget * * label,
669  GtkWidget * * icon, const char * domain)
670 {
671  if (widget->data.label.stock_id)
672  *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
673 
674  * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
675  gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
676 
677  if (widget->data.label.single_line == FALSE)
678  gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE);
679 
680  gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
681 }
682 
683 static void create_cbox (const PreferencesWidget * widget, GtkWidget * * label,
684  GtkWidget * * combobox, const char * domain)
685 {
686  * combobox = gtk_combo_box_text_new ();
687 
688  if (widget->label) {
689  * label = gtk_label_new (dgettext (domain, widget->label));
690  }
691 
692  fill_cbox (* combobox, widget, domain);
693 }
694 
695 static void fill_grid (GtkWidget * grid, const PreferencesWidget * elements, int
696  amt, const char * domain)
697 {
698  int x;
699  GtkWidget *widget_left, *widget_middle, *widget_right;
700 
701  for (x = 0; x < amt; ++x) {
702  widget_left = widget_middle = widget_right = NULL;
703  switch (elements[x].type) {
704  case WIDGET_SPIN_BTN:
705  create_spin_button (& elements[x], & widget_left,
706  & widget_middle, & widget_right, domain);
707  break;
708  case WIDGET_LABEL:
709  create_label (& elements[x], & widget_middle, & widget_left,
710  domain);
711  break;
712  case WIDGET_FONT_BTN:
713  create_font_btn (& elements[x], & widget_left, & widget_middle,
714  domain);
715  break;
716  case WIDGET_ENTRY:
717  create_entry (& elements[x], & widget_left, & widget_middle,
718  domain);
719  break;
720  case WIDGET_COMBO_BOX:
721  create_cbox (& elements[x], & widget_left, & widget_middle,
722  domain);
723  break;
724  default:
725  g_warning("Unsupported widget type %d in table", elements[x].type);
726  }
727 
728  if (widget_left)
729  gtk_grid_attach(GTK_GRID(grid), widget_left, 0, x, 1, 1);
730 
731  if (widget_middle)
732  gtk_grid_attach(GTK_GRID(grid), widget_middle, 1, x, 1, 1);
733 
734  if (widget_right)
735  gtk_grid_attach(GTK_GRID(grid), widget_right, 2, x, 1, 1);
736  }
737 }
738 
739 /* box: a GtkBox */
740 void create_widgets_with_domain (void * box, const PreferencesWidget * widgets,
741  int amt, const char * domain)
742 {
743  GtkWidget *alignment = NULL, *widget = NULL;
744  GtkWidget *child_box = NULL;
745  GSList *radio_btn_group = NULL;
746 
747  for (int x = 0; x < amt; x ++)
748  {
749  GtkWidget * label = NULL;
750 
751  if (widget && widgets[x].child)
752  {
753  if (!child_box) {
754  child_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
755  g_object_set_data(G_OBJECT(widget), "child", child_box);
756  alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
757  gtk_box_pack_start(box, alignment, FALSE, FALSE, 0);
758  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
759  gtk_container_add (GTK_CONTAINER (alignment), child_box);
760 
761  if (GTK_IS_TOGGLE_BUTTON (widget))
762  gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget));
763  }
764  } else
765  child_box = NULL;
766 
767  alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
768  gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
769  gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0);
770 
771  if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN)
772  radio_btn_group = NULL;
773 
774  switch(widgets[x].type) {
775  case WIDGET_CHK_BTN:
776  widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label));
777  init_toggle_button (widget, & widgets[x]);
778  break;
779  case WIDGET_LABEL:
780  if (strstr (widgets[x].label, "<b>"))
781  gtk_alignment_set_padding ((GtkAlignment *) alignment,
782  (x == 0) ? 0 : 12, 0, 0, 0);
783 
784  GtkWidget * icon = NULL;
785  create_label (& widgets[x], & label, & icon, domain);
786 
787  if (icon == NULL)
788  widget = label;
789  else {
790  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
791  gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0);
792  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
793  }
794  break;
795  case WIDGET_RADIO_BTN:
796  widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
797  dgettext (domain, widgets[x].label));
798  radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
799  init_radio_button (widget, & widgets[x]);
800  break;
801  case WIDGET_SPIN_BTN:
802  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
803 
804  GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL;
805  create_spin_button (& widgets[x], & label_pre, & spin_btn,
806  & label_past, domain);
807 
808  if (label_pre)
809  gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0);
810  if (spin_btn)
811  gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0);
812  if (label_past)
813  gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0);
814 
815  break;
816  case WIDGET_CUSTOM: /* custom widget. --nenolod */
817  if (widgets[x].data.populate)
818  widget = widgets[x].data.populate();
819  else
820  widget = NULL;
821 
822  break;
823  case WIDGET_FONT_BTN:
824  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
825 
826  GtkWidget *font_btn = NULL;
827  create_font_btn (& widgets[x], & label, & font_btn, domain);
828 
829  if (label)
830  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
831  if (font_btn)
832  gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0);
833  break;
834  case WIDGET_TABLE:
835  widget = gtk_grid_new();
836  fill_grid(widget, widgets[x].data.table.elem,
837  widgets[x].data.table.rows, domain);
838  gtk_grid_set_column_spacing(GTK_GRID(widget), 6);
839  gtk_grid_set_row_spacing(GTK_GRID(widget), 6);
840  break;
841  case WIDGET_ENTRY:
842  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
843 
844  GtkWidget *entry = NULL;
845  create_entry (& widgets[x], & label, & entry, domain);
846 
847  if (label)
848  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
849  if (entry)
850  gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
851  break;
852  case WIDGET_COMBO_BOX:
853  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
854 
855  GtkWidget *combo = NULL;
856  create_cbox (& widgets[x], & label, & combo, domain);
857 
858  if (label)
859  gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
860  if (combo)
861  gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0);
862  break;
863  case WIDGET_BOX:
864  gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0);
865 
866  if (widgets[x].data.box.horizontal) {
867  widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
868  } else {
869  widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
870  }
871 
872  create_widgets_with_domain ((GtkBox *) widget,
873  widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain);
874 
875  if (widgets[x].data.box.frame) {
876  GtkWidget *tmp;
877  tmp = widget;
878 
879  widget = gtk_frame_new (dgettext (domain, widgets[x].label));
880  gtk_container_add(GTK_CONTAINER(widget), tmp);
881  }
882  break;
883  case WIDGET_NOTEBOOK:
884  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
885 
886  widget = gtk_notebook_new();
887 
888  int i;
889  for (i = 0; i<widgets[x].data.notebook.n_tabs; i++) {
890  GtkWidget *vbox;
891  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5);
892  create_widgets_with_domain ((GtkBox *) vbox,
893  widgets[x].data.notebook.tabs[i].widgets,
894  widgets[x].data.notebook.tabs[i].n_widgets, domain);
895 
896  gtk_notebook_append_page (GTK_NOTEBOOK (widget), vbox,
897  gtk_label_new (dgettext (domain,
898  widgets[x].data.notebook.tabs[i].name)));
899  }
900  break;
901  case WIDGET_SEPARATOR:
902  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 0, 0);
903 
904  if (widgets[x].data.separator.horizontal == TRUE) {
905  widget = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
906  } else {
907  widget = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
908  }
909  break;
910  default:
911  break;
912  }
913 
914  if (widget && !gtk_widget_get_parent(widget))
915  gtk_container_add(GTK_CONTAINER(alignment), widget);
916  if (widget && widgets[x].tooltip && widgets[x].type != WIDGET_SPIN_BTN)
917  gtk_widget_set_tooltip_text (widget, dgettext (domain,
918  widgets[x].tooltip));
919  }
920 
921 }
922 
923 static GtkWidget *
925 {
926  GtkWidget *titlestring_tag_menu, *menu_item;
927  unsigned int i;
928 
929  titlestring_tag_menu = gtk_menu_new();
930  for(i = 0; i < n_title_field_tags; i++) {
931  menu_item = gtk_menu_item_new_with_label(_(title_field_tags[i].name));
932  gtk_menu_shell_append(GTK_MENU_SHELL(titlestring_tag_menu), menu_item);
933  g_signal_connect(menu_item, "activate",
934  G_CALLBACK(titlestring_tag_menu_callback),
935  GINT_TO_POINTER(i));
936  };
937  gtk_widget_show_all(titlestring_tag_menu);
938 
939  return titlestring_tag_menu;
940 }
941 
942 static void show_numbers_cb (GtkToggleButton * numbers, void * unused)
943 {
944  set_bool (NULL, "show_numbers_in_pl", gtk_toggle_button_get_active (numbers));
946  hook_call ("title change", NULL);
947 }
948 
949 static void leading_zero_cb (GtkToggleButton * leading)
950 {
951  set_bool (NULL, "leading_zero", gtk_toggle_button_get_active (leading));
953  hook_call ("title change", NULL);
954 }
955 
956 static void create_titlestring_widgets (GtkWidget * * cbox, GtkWidget * * entry)
957 {
958  * cbox = gtk_combo_box_text_new ();
959  for (int i = 0; i < TITLESTRING_NPRESETS; i ++)
960  gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _(titlestring_preset_names[i]));
961  gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _("Custom"));
962 
963  * entry = gtk_entry_new ();
964 
965  char * format = get_string (NULL, "generic_title_format");
966  update_titlestring_cbox ((GtkComboBox *) * cbox, format);
967  gtk_entry_set_text ((GtkEntry *) * entry, format);
968  g_free (format);
969 
970  g_signal_connect (* cbox, "changed", (GCallback) on_titlestring_cbox_changed, * entry);
971  g_signal_connect (* entry, "changed", (GCallback) on_titlestring_entry_changed, * cbox);
972 }
973 
974 static void
976 {
977  GtkWidget *vbox5;
978  GtkWidget *alignment55;
979  GtkWidget *label60;
980  GtkWidget *alignment56;
981  GtkWidget *grid6;
982  GtkWidget *titlestring_help_button;
983  GtkWidget *image1;
984  GtkWidget *label62;
985  GtkWidget *label61;
986  GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu();
987  GtkWidget * numbers_alignment, * numbers;
988 
989  vbox5 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
990  gtk_container_add ((GtkContainer *) category_notebook, vbox5);
991 
992  create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets));
993 
994  alignment55 = gtk_alignment_new (0.5, 0.5, 1, 1);
995  gtk_box_pack_start (GTK_BOX (vbox5), alignment55, FALSE, FALSE, 0);
996  gtk_alignment_set_padding ((GtkAlignment *) alignment55, 12, 3, 0, 0);
997 
998  label60 = gtk_label_new (_("<b>Song Display</b>"));
999  gtk_container_add (GTK_CONTAINER (alignment55), label60);
1000  gtk_label_set_use_markup (GTK_LABEL (label60), TRUE);
1001  gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5);
1002 
1003  numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
1004  gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
1005  gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
1006 
1007  numbers = gtk_check_button_new_with_label (_("Show song numbers"));
1008  gtk_toggle_button_set_active ((GtkToggleButton *) numbers,
1009  get_bool (NULL, "show_numbers_in_pl"));
1010  g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
1011  show_numbers_cb, 0);
1012  gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
1013 
1014  numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
1015  gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
1016  gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
1017 
1018  numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 "
1019  "instead of 2:00)"));
1020  gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero"));
1021  g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
1022  leading_zero_cb, 0);
1023  gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
1024 
1025  alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1);
1026  gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0);
1027  gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0);
1028 
1029  grid6 = gtk_grid_new ();
1030  gtk_container_add (GTK_CONTAINER (alignment56), grid6);
1031  gtk_grid_set_row_spacing (GTK_GRID (grid6), 4);
1032  gtk_grid_set_column_spacing (GTK_GRID (grid6), 12);
1033 
1034  titlestring_help_button = gtk_button_new ();
1035  gtk_grid_attach (GTK_GRID (grid6), titlestring_help_button, 2, 1, 1, 1);
1036 
1037  gtk_widget_set_can_focus (titlestring_help_button, FALSE);
1038  gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format"));
1039  gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF);
1040  gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE);
1041 
1042  image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON);
1043  gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1);
1044 
1045  GtkWidget * titlestring_cbox;
1046  create_titlestring_widgets (& titlestring_cbox, & titlestring_entry);
1047  gtk_widget_set_hexpand (titlestring_cbox, TRUE);
1048  gtk_widget_set_hexpand (titlestring_entry, TRUE);
1049  gtk_grid_attach (GTK_GRID (grid6), titlestring_cbox, 1, 0, 1, 1);
1050  gtk_grid_attach (GTK_GRID (grid6), titlestring_entry, 1, 1, 1, 1);
1051 
1052  label62 = gtk_label_new (_("Custom string:"));
1053  gtk_grid_attach (GTK_GRID (grid6), label62, 0, 1, 1, 1);
1054  gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT);
1055  gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5);
1056 
1057  label61 = gtk_label_new (_("Title format:"));
1058  gtk_grid_attach (GTK_GRID (grid6), label61, 0, 0, 1, 1);
1059  gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT);
1060  gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5);
1061 
1062  g_signal_connect(titlestring_help_button, "clicked",
1064  titlestring_tag_menu);
1065 }
1066 
1067 static void create_song_info_category (void)
1068 {
1069  GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1070  gtk_container_add ((GtkContainer *) category_notebook, vbox);
1071  create_widgets ((GtkBox *) vbox, song_info_page_widgets,
1072  G_N_ELEMENTS (song_info_page_widgets));
1073 }
1074 
1076 
1077 static bool_t output_enum_cb (PluginHandle * plugin, GList * * list)
1078 {
1079  * list = g_list_prepend (* list, plugin);
1080  return TRUE;
1081 }
1082 
1083 static GList * output_get_list (void)
1084 {
1085  static GList * list = NULL;
1086 
1087  if (list == NULL)
1088  {
1090  & list);
1091  list = g_list_reverse (list);
1092  }
1093 
1094  return list;
1095 }
1096 
1097 static void output_combo_update (GtkComboBox * combo)
1098 {
1100  gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin));
1101  gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
1102  gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
1103 }
1104 
1105 static void output_combo_changed (GtkComboBox * combo)
1106 {
1107  PluginHandle * plugin = g_list_nth_data (output_get_list (),
1108  gtk_combo_box_get_active (combo));
1109  g_return_if_fail (plugin != NULL);
1110 
1111  plugin_enable (plugin, TRUE);
1112  output_combo_update (combo);
1113 }
1114 
1115 static void output_combo_fill (GtkComboBox * combo)
1116 {
1117  for (GList * node = output_get_list (); node != NULL; node = node->next)
1118  gtk_combo_box_text_append_text ((GtkComboBoxText *) combo,
1119  plugin_get_name (node->data));
1120 }
1121 
1122 static void output_bit_depth_changed (void)
1123 {
1125 }
1126 
1127 static void output_do_config (void)
1128 {
1130  g_return_if_fail (plugin != NULL);
1131  plugin_do_configure (plugin);
1132 }
1133 
1134 static void output_do_about (void)
1135 {
1137  g_return_if_fail (plugin != NULL);
1138  plugin_do_about (plugin);
1139 }
1140 
1141 static void * create_output_plugin_box (void)
1142 {
1143  GtkWidget * hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1144  gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0);
1145 
1146  GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1147  gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0);
1148 
1149  GtkWidget * hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1150  gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0);
1151 
1152  GtkWidget * output_plugin_cbox = gtk_combo_box_text_new ();
1153  gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0);
1154 
1155  GtkWidget * hbox3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1156  gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0);
1157 
1158  output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
1159  gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0);
1160 
1161  output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
1162  gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0);
1163 
1164  output_combo_fill ((GtkComboBox *) output_plugin_cbox);
1165  output_combo_update ((GtkComboBox *) output_plugin_cbox);
1166 
1167  g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL);
1168  g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL);
1169  g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL);
1170 
1171  return hbox1;
1172 }
1173 
1174 static void create_audio_category (void)
1175 {
1176  GtkWidget * audio_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1177  create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets));
1178  gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox);
1179 }
1180 
1181 static void
1183 {
1184  GtkWidget *connectivity_page_vbox;
1185  GtkWidget *vbox29;
1186 
1187  connectivity_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1188  gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox);
1189 
1190  vbox29 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1191  gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0);
1192 
1193  create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets));
1194 }
1195 
1196 static void create_plugin_category (void)
1197 {
1198  GtkWidget * notebook = gtk_notebook_new ();
1199  gtk_container_add ((GtkContainer *) category_notebook, notebook);
1200 
1203  const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"),
1204  N_("Effect"), N_("Visualization"), N_("General")};
1205 
1206  for (int i = 0; i < G_N_ELEMENTS (types); i ++)
1207  gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new
1208  (types[i]), gtk_label_new (_(names[i])));
1209 }
1210 
1211 static bool_t
1212 prefswin_destroy(GtkWidget *window, GdkEvent *event, void * data)
1213 {
1214  prefswin = NULL;
1216  gtk_widget_destroy(window);
1217  return TRUE;
1218 }
1219 
1220 static void create_prefs_window (void)
1221 {
1222  char *aud_version_string;
1223 
1224  GtkWidget *vbox;
1225  GtkWidget *hbox1;
1226  GtkWidget *scrolledwindow6;
1227  GtkWidget *hseparator1;
1228  GtkWidget *hbox4;
1229  GtkWidget *audversionlabel;
1230  GtkWidget *prefswin_button_box;
1231  GtkWidget *close;
1232  GtkAccelGroup *accel_group;
1233 
1234  accel_group = gtk_accel_group_new ();
1235 
1236  prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1237  gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG);
1238  gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12);
1239  gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences"));
1240  gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER);
1241  gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400);
1242 
1243  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1244  gtk_container_add (GTK_CONTAINER (prefswin), vbox);
1245 
1246  hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
1247  gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);
1248 
1249  scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL);
1250  gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0);
1251  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1252  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN);
1253 
1254  category_treeview = gtk_tree_view_new ();
1255  gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview);
1256  gtk_widget_set_size_request (scrolledwindow6, 168, -1);
1257  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE);
1258 
1259  category_notebook = gtk_notebook_new ();
1260  gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0);
1261 
1262  gtk_widget_set_can_focus (category_notebook, FALSE);
1263  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE);
1264  gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE);
1265  gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE);
1266 
1272 
1273  hseparator1 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
1274  gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6);
1275 
1276  hbox4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1277  gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0);
1278 
1279  audversionlabel = gtk_label_new ("");
1280  gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0);
1281  gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE);
1282 
1283  prefswin_button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
1284  gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0);
1285  gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END);
1286  gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6);
1287 
1288  close = gtk_button_new_from_stock ("gtk-close");
1289  gtk_container_add (GTK_CONTAINER (prefswin_button_box), close);
1290  gtk_widget_set_can_default(close, TRUE);
1291  gtk_widget_add_accelerator (close, "clicked", accel_group,
1292  GDK_KEY_Escape, (GdkModifierType) 0,
1293  GTK_ACCEL_VISIBLE);
1294 
1295 
1296  gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group);
1297 
1298  /* connect signals */
1299  g_signal_connect(G_OBJECT(prefswin), "delete_event",
1300  G_CALLBACK(prefswin_destroy),
1301  NULL);
1302  g_signal_connect_swapped(G_OBJECT(close), "clicked",
1303  G_CALLBACK(prefswin_destroy),
1304  prefswin);
1305 
1306  /* create category view */
1307  fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook);
1308 
1309  /* audacious version label */
1310 
1311  aud_version_string = g_strdup_printf
1312  ("<span size='small'>%s (%s)</span>", "Audacious " VERSION, BUILDSTAMP);
1313 
1314  gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string );
1315  g_free(aud_version_string);
1316  gtk_widget_show_all(vbox);
1317 }
1318 
1319 void
1321 {
1323 }
1324 
1326 {
1327  if (! prefswin)
1329 
1330  gtk_window_present ((GtkWindow *) prefswin);
1331 }
1332 
1334 {
1335  g_return_if_fail(prefswin);
1336  gtk_widget_hide(GTK_WIDGET(prefswin));
1337 }
#define TITLESTRING_NPRESETS
static void output_combo_changed(GtkComboBox *combo)
static void change_category(GtkNotebook *notebook, GtkTreeSelection *selection)
const char * icon_path
static void titlestring_tag_menu_callback(GtkMenuItem *menuitem, void *data)
static void on_font_btn_font_set(GtkFontButton *button, const PreferencesWidget *widget)
static void create_connectivity_category(void)
void playlist_reformat_titles(void)
static void create_titlestring_widgets(GtkWidget **cbox, GtkWidget **entry)
void show_prefs_window(void)
static void widget_set_string(const PreferencesWidget *widget, const char *value)
static const char *const titlestring_preset_names[TITLESTRING_NPRESETS]
#define N_(String)
Definition: i18n.h:29
static void * prefswin
static GtkWidget * category_notebook
#define _(String)
Definition: i18n.h:25
PluginHandle * output_plugin_get_current(void)
Definition: output.c:595
static void on_titlestring_entry_changed(GtkEntry *entry, GtkComboBox *cbox)
static void fill_category_list(GtkTreeView *treeview, GtkNotebook *notebook)
char * get_string(const char *section, const char *name)
Definition: config.c:270
static bool_t prefswin_destroy(GtkWidget *window, GdkEvent *event, void *data)
static void fill_grid(GtkWidget *grid, const PreferencesWidget *elements, int amt, const char *domain)
static void create_cbox(const PreferencesWidget *widget, GtkWidget **label, GtkWidget **combobox, const char *domain)
static PreferencesWidget chardet_elements[]
static PreferencesWidget proxy_host_port_elements[]
static TitleFieldTag title_field_tags[]
double get_double(const char *section, const char *name)
Definition: config.c:327
static void create_spin_button(const PreferencesWidget *widget, GtkWidget **label_pre, GtkWidget **spin_btn, GtkWidget **label_past, const char *domain)
int format
Definition: audio.c:132
void destroy_prefs_window(void)
void set_string(const char *section, const char *name, const char *value)
Definition: config.c:221
static PreferencesWidget song_info_page_widgets[]
type
Definition: plugins-api.h:41
static ComboBoxElements bitdepth_elements[]
CategoryViewCols
static bool_t output_enum_cb(PluginHandle *plugin, GList **list)
const char * name
static GtkWidget * create_titlestring_tag_menu(void)
void set_bool(const char *section, const char *name, bool_t value)
Definition: config.c:289
static void on_titlestring_help_button_clicked(GtkButton *button, void *data)
PluginHandle * plugin_get_current(int type)
Definition: plugin-init.c:213
static void on_cbox_changed_string(GtkComboBox *combobox, const PreferencesWidget *widget)
#define FALSE
Definition: core.h:35
static void create_audio_category(void)
Index Index bool_t
Definition: playlist-api.h:122
static void on_toggle_button_toggled(GtkToggleButton *button, const PreferencesWidget *widget)
static void create_plugin_category(void)
static Category categories[]
void plugin_do_configure(PluginHandle *plugin)
Definition: plugin-init.c:324
static PreferencesWidget audio_page_widgets[]
const char * name
Definition: plugin-init.c:38
static void on_spin_btn_changed_int(GtkSpinButton *button, const PreferencesWidget *widget)
static PreferencesWidget proxy_auth_elements[]
static void on_entry_changed(GtkEntry *entry, const PreferencesWidget *widget)
static PreferencesWidget rg_mode_widgets[]
void playlist_trigger_scan(void)
static int n_categories
#define NULL
Definition: core.h:27
static void create_prefs_window(void)
static void output_do_about(void)
static void create_entry(const PreferencesWidget *widget, GtkWidget **label, GtkWidget **entry, const char *domain)
void set_int(const char *section, const char *name, int value)
Definition: config.c:302
static void on_cbox_changed_int(GtkComboBox *combobox, const PreferencesWidget *widget)
bool_t plugin_has_about(PluginHandle *plugin)
const char * name
static char * widget_get_string(const PreferencesWidget *widget)
static void output_combo_fill(GtkComboBox *combo)
static void show_numbers_cb(GtkToggleButton *numbers, void *unused)
bool_t plugin_has_configure(PluginHandle *plugin)
void set_double(const char *section, const char *name, double value)
Definition: config.c:319
const char * tag
bool_t(* PluginForEachFunc)(PluginHandle *plugin, void *data)
Definition: plugins.h:27
static bool_t active
Definition: equalizer.c:53
#define TRUE
Definition: core.h:37
static void create_label(const PreferencesWidget *widget, GtkWidget **label, GtkWidget **icon, const char *domain)
static void on_radio_button_toggled(GtkWidget *button, const PreferencesWidget *widget)
bool_t get_bool(const char *section, const char *name)
Definition: config.c:294
const char PluginHandle decoder const char PluginHandle decoder const char PluginHandle decoder void const PreferencesWidget widgets
Definition: misc-api.h:103
static void fill_cbox(GtkWidget *combobox, const PreferencesWidget *widget, const char *domain)
bool_t plugin_enable(PluginHandle *plugin, bool_t enable)
Definition: plugin-init.c:272
void hide_prefs_window(void)
static void update_titlestring_cbox(GtkComboBox *cbox, const char *format)
static void output_bit_depth_changed(void)
GtkWidget * titlestring_entry
static GtkWidget * category_treeview
static void * create_output_plugin_box(void)
static void output_combo_update(GtkComboBox *combo)
const char * get_path(int id)
Definition: main.c:225
static void widget_set_double(const PreferencesWidget *widget, double value)
static void output_do_config(void)
static PreferencesWidget playlist_page_widgets[]
void create_font_btn(const PreferencesWidget *widget, GtkWidget **label, GtkWidget **font_btn, const char *domain)
void create_widgets_with_domain(void *box, const PreferencesWidget *widgets, int amt, const char *domain)
static GList * output_get_list(void)
static GtkWidget * output_about_button
static void widget_set_bool(const PreferencesWidget *widget, bool_t value)
void plugin_do_about(PluginHandle *plugin)
Definition: plugin-init.c:312
void output_reset(int type)
Definition: output.c:507
static void widget_set_int(const PreferencesWidget *widget, int value)
EXPORT void hook_call(const char *name, void *data)
Definition: hook.c:98
static void create_song_info_category(void)
static int widget_get_int(const PreferencesWidget *widget)
const char PluginHandle decoder const char PluginHandle decoder const char PluginHandle decoder void box
Definition: misc-api.h:103
int get_int(const char *section, const char *name)
Definition: config.c:310
static const char *const titlestring_presets[TITLESTRING_NPRESETS]
static double widget_get_double(const PreferencesWidget *widget)
static PreferencesWidget connectivity_page_widgets[]
void data PluginHandle plugin
Definition: plugins-api.h:54
static void on_titlestring_cbox_changed(GtkComboBox *cbox, GtkEntry *entry)
static void leading_zero_cb(GtkToggleButton *leading)
static void init_toggle_button(GtkWidget *button, const PreferencesWidget *widget)
void plugin_for_each(int type, PluginForEachFunc func, void *data)
const char * plugin_get_name(PluginHandle *plugin)
static bool_t widget_get_bool(const PreferencesWidget *widget)
static void create_playlist_category(void)
GtkWidget * plugin_view_new(int type)
Definition: plugin-view.c:220
static GtkWidget * output_config_button
Index Index play entry
Definition: playlist-api.h:144
static void init_radio_button(GtkWidget *button, const PreferencesWidget *widget)
static void on_spin_btn_changed_float(GtkSpinButton *button, const PreferencesWidget *widget)
static const unsigned int n_title_field_tags