Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
ui_preferences.c
Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2011  Audacious development team.
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; under version 3 of the License.
00007  *
00008  *  This program is distributed in the hope that it will be useful,
00009  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  *  GNU General Public License for more details.
00012  *
00013  *  You should have received a copy of the GNU General Public License
00014  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00015  *
00016  *  The Audacious team does not consider modular code linking to
00017  *  Audacious or using our public API to be a derived work.
00018  */
00019 
00020 #include <string.h>
00021 #include <stdio.h>
00022 
00023 #include <gdk/gdkkeysyms.h>
00024 #include <gtk/gtk.h>
00025 
00026 #include <libaudcore/hook.h>
00027 
00028 #include "config.h"
00029 #include "debug.h"
00030 #include "gtk-compat.h"
00031 #include "i18n.h"
00032 #include "misc.h"
00033 #include "output.h"
00034 #include "playback.h"
00035 #include "playlist.h"
00036 #include "plugin.h"
00037 #include "plugins.h"
00038 #include "preferences.h"
00039 #include "ui_preferences.h"
00040 
00041 #ifdef USE_CHARDET
00042 #include <libguess.h>
00043 #endif
00044 
00045 static void sw_volume_toggled (void);
00046 
00047 enum CategoryViewCols {
00048     CATEGORY_VIEW_COL_ICON,
00049     CATEGORY_VIEW_COL_NAME,
00050     CATEGORY_VIEW_COL_ID,
00051     CATEGORY_VIEW_N_COLS
00052 };
00053 
00054 typedef struct {
00055     const char *icon_path;
00056     const char *name;
00057 } Category;
00058 
00059 typedef struct {
00060     const char *name;
00061     const char *tag;
00062 } TitleFieldTag;
00063 
00064 static /* GtkWidget * */ void * prefswin = NULL;
00065 static GtkWidget *filepopup_settings = NULL;
00066 static GtkWidget *category_treeview = NULL;
00067 static GtkWidget *category_notebook = NULL;
00068 GtkWidget *filepopupbutton = NULL;
00069 
00070 /* filepopup settings widgets */
00071 GtkWidget *filepopup_cover_name_include;
00072 GtkWidget *filepopup_cover_name_exclude;
00073 GtkWidget *filepopup_recurse;
00074 GtkWidget *filepopup_recurse_depth;
00075 GtkWidget *filepopup_recurse_depth_box;
00076 GtkWidget *filepopup_use_file_cover;
00077 GtkWidget *filepopup_showprogressbar;
00078 GtkWidget *filepopup_delay;
00079 
00080 /* prefswin widgets */
00081 GtkWidget *titlestring_entry;
00082 GtkWidget *filepopup_settings_button;
00083 
00084 static Category categories[] = {
00085  {"audio.png", N_("Audio")},
00086  {"connectivity.png", N_("Network")},
00087  {"playlist.png", N_("Playlist")},
00088  {"plugins.png", N_("Plugins")},
00089 };
00090 
00091 static int n_categories = G_N_ELEMENTS(categories);
00092 
00093 static TitleFieldTag title_field_tags[] = {
00094     { N_("Artist")     , "${artist}" },
00095     { N_("Album")      , "${album}" },
00096     { N_("Title")      , "${title}" },
00097     { N_("Tracknumber"), "${track-number}" },
00098     { N_("Genre")      , "${genre}" },
00099     { N_("Filename")   , "${file-name}" },
00100     { N_("Filepath")   , "${file-path}" },
00101     { N_("Date")       , "${date}" },
00102     { N_("Year")       , "${year}" },
00103     { N_("Comment")    , "${comment}" },
00104     { N_("Codec")      , "${codec}" },
00105     { N_("Quality")    , "${quality}" },
00106 };
00107 static const unsigned int n_title_field_tags = G_N_ELEMENTS(title_field_tags);
00108 
00109 #ifdef USE_CHARDET
00110 static ComboBoxElements chardet_detector_presets[] = {
00111  {"", N_("None")},
00112  {GUESS_REGION_AR, N_("Arabic")},
00113  {GUESS_REGION_BL, N_("Baltic")},
00114  {GUESS_REGION_CN, N_("Chinese")},
00115  {GUESS_REGION_GR, N_("Greek")},
00116  {GUESS_REGION_HW, N_("Hebrew")},
00117  {GUESS_REGION_JP, N_("Japanese")},
00118  {GUESS_REGION_KR, N_("Korean")},
00119  {GUESS_REGION_PL, N_("Polish")},
00120  {GUESS_REGION_RU, N_("Russian")},
00121  {GUESS_REGION_TW, N_("Taiwanese")},
00122  {GUESS_REGION_TR, N_("Turkish")}};
00123 #endif
00124 
00125 static ComboBoxElements bitdepth_elements[] = {
00126     { GINT_TO_POINTER(16), "16" },
00127     { GINT_TO_POINTER(24), "24" },
00128     { GINT_TO_POINTER(32), "32" },
00129     {GINT_TO_POINTER (0), "Floating point"},
00130 };
00131 
00132 typedef struct {
00133     void *next;
00134     GtkWidget *container;
00135     const char * pg_name;
00136     const char * img_url;
00137 } CategoryQueueEntry;
00138 
00139 CategoryQueueEntry *category_queue = NULL;
00140 
00141 static void * create_output_plugin_box (void);
00142 
00143 static PreferencesWidget rg_mode_widgets[] = {
00144  {WIDGET_CHK_BTN, N_("Album mode"), .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}};
00145 
00146 static PreferencesWidget audio_page_widgets[] = {
00147  {WIDGET_LABEL, N_("<b>Output Settings</b>")},
00148  {WIDGET_CUSTOM, .data = {.populate = create_output_plugin_box}},
00149  {WIDGET_COMBO_BOX, N_("Bit depth:"),
00150   .cfg_type = VALUE_INT, .cname = "output_bit_depth",
00151   .data = {.combo = {bitdepth_elements, G_N_ELEMENTS (bitdepth_elements), TRUE}}},
00152  {WIDGET_SPIN_BTN, N_("Buffer size:"),
00153   .cfg_type = VALUE_INT, .cname = "output_buffer_size",
00154   .data = {.spin_btn = {100, 10000, 1000, N_("ms")}}},
00155  {WIDGET_CHK_BTN, N_("Use software volume control (not recommended)"),
00156   .cfg_type = VALUE_BOOLEAN, .cname = "software_volume_control", .callback = sw_volume_toggled},
00157  {WIDGET_LABEL, N_("<b>Replay Gain</b>")},
00158  {WIDGET_CHK_BTN, N_("Enable Replay Gain"),
00159   .cfg_type = VALUE_BOOLEAN, .cname = "enable_replay_gain"},
00160  {WIDGET_BOX, .child = TRUE, .data = {.box = {rg_mode_widgets, G_N_ELEMENTS (rg_mode_widgets), TRUE}}},
00161  {WIDGET_CHK_BTN, N_("Prevent clipping (recommended)"), .child = TRUE,
00162   .cfg_type = VALUE_BOOLEAN, .cname = "enable_clipping_prevention"},
00163  {WIDGET_LABEL, N_("<b>Adjust Levels</b>"), .child = TRUE},
00164  {WIDGET_SPIN_BTN, N_("Amplify all files:"), .child = TRUE,
00165   .cfg_type = VALUE_FLOAT, .cname = "replay_gain_preamp",
00166   .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}},
00167  {WIDGET_SPIN_BTN, N_("Amplify untagged files:"), .child = TRUE,
00168   .cfg_type = VALUE_FLOAT, .cname = "default_gain",
00169   .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}};
00170 
00171 static PreferencesWidget proxy_host_port_elements[] = {
00172  {WIDGET_ENTRY, N_("Proxy hostname:"), .cfg_type = VALUE_STRING, .cname = "proxy_host"},
00173  {WIDGET_ENTRY, N_("Proxy port:"), .cfg_type = VALUE_STRING, .cname = "proxy_port"}};
00174 
00175 static PreferencesWidget proxy_auth_elements[] = {
00176  {WIDGET_ENTRY, N_("Proxy username:"), .cfg_type = VALUE_STRING, .cname = "proxy_user"},
00177  {WIDGET_ENTRY, N_("Proxy password:"), .cfg_type = VALUE_STRING, .cname = "proxy_pass",
00178   .data = {.entry = {.password = TRUE}}}};
00179 
00180 static PreferencesWidget connectivity_page_widgets[] = {
00181     {WIDGET_LABEL, N_("<b>Proxy Configuration</b>"), NULL, NULL, NULL, FALSE},
00182     {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"},
00183     {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_host_port_elements,
00184      G_N_ELEMENTS (proxy_host_port_elements)}}},
00185     {WIDGET_CHK_BTN, N_("Use authentication with proxy"),
00186      .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"},
00187     {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_auth_elements,
00188      G_N_ELEMENTS (proxy_auth_elements)}}}
00189 };
00190 
00191 static PreferencesWidget chardet_elements[] = {
00192 #ifdef USE_CHARDET
00193  {WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"),
00194   .cfg_type = VALUE_STRING, .cname = "chardet_detector", .child = TRUE,
00195   .data = {.combo = {chardet_detector_presets,
00196   G_N_ELEMENTS (chardet_detector_presets), TRUE}}},
00197 #endif
00198  {WIDGET_ENTRY, N_("Fallback character encodings:"), .cfg_type = VALUE_STRING,
00199   .cname = "chardet_fallback", .child = TRUE}};
00200 
00201 static PreferencesWidget playlist_page_widgets[] = {
00202     {WIDGET_LABEL, N_("<b>Behavior</b>"), NULL, NULL, NULL, FALSE},
00203     {WIDGET_CHK_BTN, N_("Continue playback on startup"),
00204      .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"},
00205     {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"),
00206      .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"},
00207     {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"),
00208      .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"},
00209     {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"),
00210      .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"},
00211     {WIDGET_LABEL, N_("<b>Metadata</b>"), NULL, NULL, NULL, FALSE},
00212     {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"),
00213      .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play",
00214      .callback = playlist_trigger_scan},
00215     {WIDGET_TABLE, .data = {.table = {chardet_elements,
00216      G_N_ELEMENTS (chardet_elements)}}}
00217 };
00218 
00219 #define TITLESTRING_NPRESETS 6
00220 
00221 static const char * const titlestring_presets[TITLESTRING_NPRESETS] = {
00222  "${title}",
00223  "${?artist:${artist} - }${title}",
00224  "${?artist:${artist} - }${?album:${album} - }${title}",
00225  "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}",
00226  "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}",
00227  "${?album:${album} - }${title}"};
00228 
00229 static const char * const titlestring_preset_names[TITLESTRING_NPRESETS] = {
00230  N_("TITLE"),
00231  N_("ARTIST - TITLE"),
00232  N_("ARTIST - ALBUM - TITLE"),
00233  N_("ARTIST - ALBUM - TRACK. TITLE"),
00234  N_("ARTIST [ ALBUM ] - TRACK. TITLE"),
00235  N_("ALBUM - TITLE")};
00236 
00237 static void prefswin_page_queue_destroy(CategoryQueueEntry *ent);
00238 
00239 static void
00240 change_category(GtkNotebook * notebook,
00241                 GtkTreeSelection * selection)
00242 {
00243     GtkTreeModel *model;
00244     GtkTreeIter iter;
00245     int index;
00246 
00247     if (!gtk_tree_selection_get_selected(selection, &model, &iter))
00248         return;
00249 
00250     gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
00251     gtk_notebook_set_current_page(notebook, index);
00252 }
00253 
00254 static void
00255 editable_insert_text(GtkEditable * editable,
00256                      const char * text,
00257                      int * pos)
00258 {
00259     gtk_editable_insert_text(editable, text, strlen(text), pos);
00260 }
00261 
00262 static void
00263 titlestring_tag_menu_callback(GtkMenuItem * menuitem,
00264                               gpointer data)
00265 {
00266     const char *separator = " - ";
00267     int item = GPOINTER_TO_INT(data);
00268     int pos;
00269 
00270     pos = gtk_editable_get_position(GTK_EDITABLE(titlestring_entry));
00271 
00272     /* insert separator as needed */
00273     if (g_utf8_strlen(gtk_entry_get_text(GTK_ENTRY(titlestring_entry)), -1) > 0)
00274         editable_insert_text(GTK_EDITABLE(titlestring_entry), separator, &pos);
00275 
00276     editable_insert_text(GTK_EDITABLE(titlestring_entry), _(title_field_tags[item].tag),
00277                          &pos);
00278 
00279     gtk_editable_set_position(GTK_EDITABLE(titlestring_entry), pos);
00280 }
00281 
00282 static void
00283 on_titlestring_help_button_clicked(GtkButton * button,
00284                                    gpointer data)
00285 {
00286     GtkMenu * menu = data;
00287     gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
00288 }
00289 
00290 static void update_titlestring_cbox (GtkComboBox * cbox, const char * format)
00291 {
00292     int preset;
00293     for (preset = 0; preset < TITLESTRING_NPRESETS; preset ++)
00294     {
00295         if (! strcmp (titlestring_presets[preset], format))
00296             break;
00297     }
00298 
00299     if (gtk_combo_box_get_active (cbox) != preset)
00300         gtk_combo_box_set_active (cbox, preset);
00301 }
00302 
00303 static void on_titlestring_entry_changed (GtkEntry * entry, GtkComboBox * cbox)
00304 {
00305     const char * format = gtk_entry_get_text (entry);
00306     set_string (NULL, "generic_title_format", format);
00307     update_titlestring_cbox (cbox, format);
00308     playlist_reformat_titles ();
00309 }
00310 
00311 static void on_titlestring_cbox_changed (GtkComboBox * cbox, GtkEntry * entry)
00312 {
00313     int preset = gtk_combo_box_get_active (cbox);
00314     if (preset < TITLESTRING_NPRESETS)
00315         gtk_entry_set_text (entry, titlestring_presets[preset]);
00316 }
00317 
00318 static void widget_set_bool (PreferencesWidget * widget, bool_t value)
00319 {
00320     g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN);
00321 
00322     if (widget->cfg)
00323         * (bool_t *) widget->cfg = value;
00324     else if (widget->cname)
00325         set_bool (widget->csect, widget->cname, value);
00326 
00327     if (widget->callback)
00328         widget->callback ();
00329 }
00330 
00331 static bool_t widget_get_bool (PreferencesWidget * widget)
00332 {
00333     g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE);
00334 
00335     if (widget->cfg)
00336         return * (bool_t *) widget->cfg;
00337     else if (widget->cname)
00338         return get_bool (widget->csect, widget->cname);
00339     else
00340         return FALSE;
00341 }
00342 
00343 static void widget_set_int (PreferencesWidget * widget, int value)
00344 {
00345     g_return_if_fail (widget->cfg_type == VALUE_INT);
00346 
00347     if (widget->cfg)
00348         * (int *) widget->cfg = value;
00349     else if (widget->cname)
00350         set_int (widget->csect, widget->cname, value);
00351 
00352     if (widget->callback)
00353         widget->callback ();
00354 }
00355 
00356 static int widget_get_int (PreferencesWidget * widget)
00357 {
00358     g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0);
00359 
00360     if (widget->cfg)
00361         return * (int *) widget->cfg;
00362     else if (widget->cname)
00363         return get_int (widget->csect, widget->cname);
00364     else
00365         return 0;
00366 }
00367 
00368 static void widget_set_double (PreferencesWidget * widget, double value)
00369 {
00370     g_return_if_fail (widget->cfg_type == VALUE_FLOAT);
00371 
00372     if (widget->cfg)
00373         * (float *) widget->cfg = value;
00374     else if (widget->cname)
00375         set_double (widget->csect, widget->cname, value);
00376 
00377     if (widget->callback)
00378         widget->callback ();
00379 }
00380 
00381 static double widget_get_double (PreferencesWidget * widget)
00382 {
00383     g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0);
00384 
00385     if (widget->cfg)
00386         return * (float *) widget->cfg;
00387     else if (widget->cname)
00388         return get_double (widget->csect, widget->cname);
00389     else
00390         return 0;
00391 }
00392 
00393 static void widget_set_string (PreferencesWidget * widget, const char * value)
00394 {
00395     g_return_if_fail (widget->cfg_type == VALUE_STRING);
00396 
00397     if (widget->cfg)
00398     {
00399         g_free (* (char * *) widget->cfg);
00400         * (char * *) widget->cfg = g_strdup (value);
00401     }
00402     else if (widget->cname)
00403         set_string (widget->csect, widget->cname, value);
00404 
00405     if (widget->callback)
00406         widget->callback ();
00407 }
00408 
00409 static char * widget_get_string (PreferencesWidget * widget)
00410 {
00411     g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL);
00412 
00413     if (widget->cfg)
00414         return g_strdup (* (char * *) widget->cfg);
00415     else if (widget->cname)
00416         return get_string (widget->csect, widget->cname);
00417     else
00418         return NULL;
00419 }
00420 
00421 static void on_font_btn_font_set (GtkFontButton * button, PreferencesWidget * widget)
00422 {
00423     widget_set_string (widget, gtk_font_button_get_font_name (button));
00424 }
00425 
00426 static void
00427 plugin_preferences_ok(GtkWidget *widget, PluginPreferences *settings)
00428 {
00429     if (settings->apply)
00430         settings->apply();
00431 
00432     gtk_widget_destroy(GTK_WIDGET(settings->data));
00433 }
00434 
00435 static void
00436 plugin_preferences_apply(GtkWidget *widget, PluginPreferences *settings)
00437 {
00438     if (settings->apply)
00439         settings->apply();
00440 }
00441 
00442 static void
00443 plugin_preferences_cancel(GtkWidget *widget, PluginPreferences *settings)
00444 {
00445     if (settings->cancel)
00446         settings->cancel();
00447 
00448     gtk_widget_destroy(GTK_WIDGET(settings->data));
00449 }
00450 
00451 static void plugin_preferences_destroy(GtkWidget *widget, PluginPreferences *settings)
00452 {
00453     gtk_widget_destroy(widget);
00454 
00455     if (settings->cleanup)
00456         settings->cleanup();
00457 
00458     settings->data = NULL;
00459 }
00460 
00461 void plugin_preferences_show (PluginPreferences * settings)
00462 {
00463     GtkWidget *window;
00464     GtkWidget *vbox, *bbox, *ok, *apply, *cancel;
00465 
00466     if (settings->data != NULL) {
00467         gtk_widget_show(GTK_WIDGET(settings->data));
00468         return;
00469     }
00470 
00471     if (settings->init)
00472         settings->init();
00473 
00474     const char * d = settings->domain;
00475     if (! d)
00476     {
00477         printf ("WARNING: PluginPreferences window with title \"%s\" did not "
00478          "declare its gettext domain.  Text may not be translated correctly.\n",
00479          settings->title);
00480         d = "audacious-plugins";
00481     }
00482 
00483     window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00484     gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
00485 
00486     if (settings->title)
00487         gtk_window_set_title ((GtkWindow *) window, dgettext (d, settings->title));
00488 
00489     gtk_container_set_border_width(GTK_CONTAINER(window), 10);
00490     g_signal_connect(G_OBJECT(window), "destroy",
00491                      G_CALLBACK(plugin_preferences_destroy), settings);
00492 
00493     vbox = gtk_vbox_new(FALSE, 10);
00494     create_widgets_with_domain ((GtkBox *) vbox, settings->prefs,
00495      settings->n_prefs, d);
00496     gtk_container_add(GTK_CONTAINER(window), vbox);
00497 
00498     bbox = gtk_hbutton_box_new();
00499     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
00500     gtk_box_set_spacing(GTK_BOX(bbox), 5);
00501     gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
00502 
00503     ok = gtk_button_new_from_stock(GTK_STOCK_OK);
00504     g_signal_connect(G_OBJECT(ok), "clicked",
00505                      G_CALLBACK(plugin_preferences_ok), settings);
00506     gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
00507     gtk_widget_set_can_default (ok, TRUE);
00508     gtk_widget_grab_default(ok);
00509 
00510     apply = gtk_button_new_from_stock(GTK_STOCK_APPLY);
00511     g_signal_connect(G_OBJECT(apply), "clicked",
00512                      G_CALLBACK(plugin_preferences_apply), settings);
00513     gtk_box_pack_start(GTK_BOX(bbox), apply, TRUE, TRUE, 0);
00514 
00515     cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
00516     g_signal_connect(G_OBJECT(cancel), "clicked",
00517                      G_CALLBACK(plugin_preferences_cancel), settings);
00518     gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
00519 
00520     gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(prefswin));
00521     gtk_widget_show_all(window);
00522     settings->data = (gpointer)window;
00523 }
00524 
00525 void plugin_preferences_cleanup (PluginPreferences * p)
00526 {
00527     if (p->data != NULL)
00528     {
00529         gtk_widget_destroy (p->data);
00530         p->data = NULL;
00531     }
00532 }
00533 
00534 static void on_spin_btn_changed_int (GtkSpinButton * button, PreferencesWidget * widget)
00535 {
00536     widget_set_int (widget, gtk_spin_button_get_value_as_int (button));
00537 }
00538 
00539 static void on_spin_btn_changed_float (GtkSpinButton * button, PreferencesWidget * widget)
00540 {
00541     widget_set_double (widget, gtk_spin_button_get_value (button));
00542 }
00543 
00544 static void fill_category_list (GtkTreeView * treeview, GtkNotebook * notebook)
00545 {
00546     GtkListStore *store;
00547     GtkCellRenderer *renderer;
00548     GtkTreeViewColumn *column;
00549     GtkTreeSelection *selection;
00550     GtkTreeIter iter;
00551     GdkPixbuf *img;
00552     CategoryQueueEntry *qlist;
00553     int i;
00554 
00555     column = gtk_tree_view_column_new();
00556     gtk_tree_view_column_set_title(column, _("Category"));
00557     gtk_tree_view_append_column(treeview, column);
00558     gtk_tree_view_column_set_spacing(column, 2);
00559 
00560     renderer = gtk_cell_renderer_pixbuf_new();
00561     gtk_tree_view_column_pack_start(column, renderer, FALSE);
00562     gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", 0, NULL);
00563 
00564     renderer = gtk_cell_renderer_text_new();
00565     gtk_tree_view_column_pack_start(column, renderer, FALSE);
00566     gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
00567 
00568     g_object_set ((GObject *) renderer, "wrap-width", 96, "wrap-mode",
00569      PANGO_WRAP_WORD_CHAR, NULL);
00570 
00571     store = gtk_list_store_new(CATEGORY_VIEW_N_COLS,
00572                                GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
00573     gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
00574 
00575     for (i = 0; i < n_categories; i ++)
00576     {
00577         char * path = g_strdup_printf ("%s/images/%s",
00578          get_path (AUD_PATH_DATA_DIR), categories[i].icon_path);
00579         img = gdk_pixbuf_new_from_file (path, NULL);
00580         g_free (path);
00581 
00582         gtk_list_store_append(store, &iter);
00583         gtk_list_store_set(store, &iter,
00584                            CATEGORY_VIEW_COL_ICON, img,
00585                            CATEGORY_VIEW_COL_NAME,
00586                            gettext(categories[i].name), CATEGORY_VIEW_COL_ID,
00587                            i, -1);
00588         g_object_unref(img);
00589     }
00590 
00591     selection = gtk_tree_view_get_selection(treeview);
00592 
00593     g_signal_connect_swapped(selection, "changed",
00594                              G_CALLBACK(change_category), notebook);
00595 
00596     /* mark the treeview widget as available to third party plugins */
00597     category_treeview = GTK_WIDGET(treeview);
00598 
00599     /* prefswin_page_queue_destroy already pops the queue forward for us. */
00600     for (qlist = category_queue; qlist != NULL; qlist = category_queue)
00601     {
00602         CategoryQueueEntry *ent = (CategoryQueueEntry *) qlist;
00603 
00604         prefswin_page_new(ent->container, ent->pg_name, ent->img_url);
00605         prefswin_page_queue_destroy(ent);
00606     }
00607 }
00608 
00609 static void on_show_filepopup_toggled (GtkToggleButton * button)
00610 {
00611     bool_t active = gtk_toggle_button_get_active (button);
00612     set_bool (NULL, "show_filepopup_for_tuple", active);
00613     gtk_widget_set_sensitive (filepopup_settings_button, active);
00614 }
00615 
00616 static void on_filepopup_settings_clicked (void)
00617 {
00618     char * string = get_string (NULL, "cover_name_include");
00619     gtk_entry_set_text ((GtkEntry *) filepopup_cover_name_include, string);
00620     g_free (string);
00621 
00622     string = get_string (NULL, "cover_name_exclude");
00623     gtk_entry_set_text ((GtkEntry *) filepopup_cover_name_exclude, string);
00624     g_free (string);
00625 
00626     gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_recurse,
00627      get_bool (NULL, "recurse_for_cover"));
00628     gtk_spin_button_set_value ((GtkSpinButton *) filepopup_recurse_depth,
00629      get_int (NULL, "recurse_for_cover_depth"));
00630     gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_use_file_cover,
00631      get_bool (NULL, "use_file_cover"));
00632 
00633     gtk_toggle_button_set_active ((GtkToggleButton *) filepopup_showprogressbar,
00634      get_bool (NULL, "filepopup_showprogressbar"));
00635     gtk_spin_button_set_value ((GtkSpinButton *) filepopup_delay,
00636      get_int (NULL, "filepopup_delay"));
00637 
00638     gtk_widget_show (filepopup_settings);
00639 }
00640 
00641 static void on_filepopup_ok_clicked (void)
00642 {
00643     set_string (NULL, "cover_name_include",
00644      gtk_entry_get_text ((GtkEntry *) filepopup_cover_name_include));
00645     set_string (NULL, "cover_name_exclude",
00646      gtk_entry_get_text ((GtkEntry *) filepopup_cover_name_exclude));
00647 
00648     set_bool (NULL, "recurse_for_cover",
00649      gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_recurse));
00650     set_int (NULL, "recurse_for_cover_depth",
00651      gtk_spin_button_get_value_as_int ((GtkSpinButton *) filepopup_recurse_depth));
00652     set_bool (NULL, "use_file_cover",
00653      gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_use_file_cover));
00654 
00655     set_bool (NULL, "filepopup_showprogressbar",
00656      gtk_toggle_button_get_active ((GtkToggleButton *) filepopup_showprogressbar));
00657     set_int (NULL, "filepopup_delay",
00658      gtk_spin_button_get_value_as_int ((GtkSpinButton *) filepopup_delay));
00659 
00660     gtk_widget_hide (filepopup_settings);
00661 }
00662 
00663 static void
00664 on_filepopup_cancel_clicked(GtkButton *button, gpointer data)
00665 {
00666     gtk_widget_hide(filepopup_settings);
00667 }
00668 
00669 static void on_toggle_button_toggled (GtkToggleButton * button, PreferencesWidget * widget)
00670 {
00671     bool_t active = gtk_toggle_button_get_active (button);
00672     widget_set_bool (widget, active);
00673 
00674     GtkWidget * child = g_object_get_data ((GObject *) button, "child");
00675     if (child)
00676         gtk_widget_set_sensitive (child, active);
00677 }
00678 
00679 static void init_toggle_button (GtkWidget * button, PreferencesWidget * widget)
00680 {
00681     if (widget->cfg_type != VALUE_BOOLEAN)
00682         return;
00683 
00684     gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget));
00685     g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, widget);
00686 }
00687 
00688 static void on_entry_changed (GtkEntry * entry, PreferencesWidget * widget)
00689 {
00690     widget_set_string (widget, gtk_entry_get_text (entry));
00691 }
00692 
00693 static void on_cbox_changed_int (GtkComboBox * combobox, PreferencesWidget * widget)
00694 {
00695     int position = gtk_combo_box_get_active (combobox);
00696     widget_set_int (widget, GPOINTER_TO_INT (widget->data.combo.elements[position].value));
00697 }
00698 
00699 static void on_cbox_changed_string (GtkComboBox * combobox, PreferencesWidget * widget)
00700 {
00701     int position = gtk_combo_box_get_active (combobox);
00702     widget_set_string (widget, widget->data.combo.elements[position].value);
00703 }
00704 
00705 static void fill_cbox (GtkWidget * combobox, PreferencesWidget * widget)
00706 {
00707     unsigned int i=0,index=0;
00708 
00709     for (i = 0; i < widget->data.combo.n_elements; i ++)
00710         gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox,
00711          _(widget->data.combo.elements[i].label));
00712 
00713     if (widget->data.combo.enabled) {
00714         switch (widget->cfg_type) {
00715             case VALUE_INT:
00716                 g_signal_connect(combobox, "changed",
00717                                  G_CALLBACK(on_cbox_changed_int), widget);
00718 
00719                 int ivalue = widget_get_int (widget);
00720 
00721                 for(i=0; i<widget->data.combo.n_elements; i++) {
00722                     if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue)
00723                     {
00724                         index = i;
00725                         break;
00726                     }
00727                 }
00728                 break;
00729             case VALUE_STRING:
00730                 g_signal_connect(combobox, "changed",
00731                                  G_CALLBACK(on_cbox_changed_string), widget);
00732 
00733                 char * value = widget_get_string (widget);
00734 
00735                 for(i=0; i<widget->data.combo.n_elements; i++) {
00736                     if (value && ! strcmp ((char *) widget->data.combo.elements[i].value, value))
00737                     {
00738                         index = i;
00739                         break;
00740                     }
00741                 }
00742 
00743                 g_free (value);
00744                 break;
00745             default:
00746                 break;
00747         }
00748         gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), index);
00749     } else {
00750         gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), -1);
00751         gtk_widget_set_sensitive(GTK_WIDGET(combobox), 0);
00752     }
00753 }
00754 
00755 void
00756 create_filepopup_settings(void)
00757 {
00758     GtkWidget *vbox;
00759     GtkWidget *table;
00760 
00761     GtkWidget *label_cover_retrieve;
00762     GtkWidget *label_cover_search;
00763     GtkWidget *label_exclude;
00764     GtkWidget *label_include;
00765     GtkWidget *label_search_depth;
00766     GtkWidget *label_misc;
00767     GtkWidget *label_delay;
00768 
00769     GtkAdjustment *recurse_for_cover_depth_adj;
00770     GtkAdjustment *delay_adj;
00771     GtkWidget *alignment;
00772 
00773     GtkWidget *hbox;
00774     GtkWidget *hbuttonbox;
00775     GtkWidget *btn_cancel;
00776     GtkWidget *btn_ok;
00777 
00778     filepopup_settings = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00779     gtk_container_set_border_width(GTK_CONTAINER(filepopup_settings), 12);
00780     gtk_window_set_title(GTK_WINDOW(filepopup_settings), _("Popup Information Settings"));
00781     gtk_window_set_position(GTK_WINDOW(filepopup_settings), GTK_WIN_POS_CENTER_ON_PARENT);
00782     gtk_window_set_skip_taskbar_hint(GTK_WINDOW(filepopup_settings), TRUE);
00783     gtk_window_set_type_hint(GTK_WINDOW(filepopup_settings), GDK_WINDOW_TYPE_HINT_DIALOG);
00784     gtk_window_set_transient_for(GTK_WINDOW(filepopup_settings), GTK_WINDOW(prefswin));
00785 
00786     vbox = gtk_vbox_new(FALSE, 12);
00787     gtk_container_add(GTK_CONTAINER(filepopup_settings), vbox);
00788 
00789     label_cover_retrieve = gtk_label_new(_("<b>Cover image retrieve</b>"));
00790     gtk_box_pack_start(GTK_BOX(vbox), label_cover_retrieve, FALSE, FALSE, 0);
00791     gtk_label_set_use_markup(GTK_LABEL(label_cover_retrieve), TRUE);
00792     gtk_misc_set_alignment(GTK_MISC(label_cover_retrieve), 0, 0.5);
00793 
00794     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."));
00795     gtk_box_pack_start(GTK_BOX(vbox), label_cover_search, FALSE, FALSE, 0);
00796     gtk_label_set_line_wrap(GTK_LABEL(label_cover_search), TRUE);
00797     gtk_misc_set_alignment(GTK_MISC(label_cover_search), 0, 0);
00798     gtk_misc_set_padding(GTK_MISC(label_cover_search), 12, 0);
00799 
00800     table = gtk_table_new(2, 2, FALSE);
00801     gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
00802     gtk_table_set_row_spacings(GTK_TABLE(table), 4);
00803     gtk_table_set_col_spacings(GTK_TABLE(table), 4);
00804 
00805     filepopup_cover_name_include = gtk_entry_new();
00806     gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_include, 1, 2, 0, 1,
00807                      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
00808                      (GtkAttachOptions) (0), 0, 0);
00809     gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_include), TRUE);
00810 
00811     label_exclude = gtk_label_new(_("Exclude:"));
00812     gtk_table_attach(GTK_TABLE(table), label_exclude, 0, 1, 1, 2,
00813                      (GtkAttachOptions) (0),
00814                      (GtkAttachOptions) (0), 0, 0);
00815     gtk_misc_set_alignment(GTK_MISC(label_exclude), 0, 0.5);
00816     gtk_misc_set_padding(GTK_MISC(label_exclude), 12, 0);
00817 
00818     label_include = gtk_label_new(_("Include:"));
00819     gtk_table_attach(GTK_TABLE(table), label_include, 0, 1, 0, 1,
00820                      (GtkAttachOptions) (0),
00821                      (GtkAttachOptions) (0), 0, 0);
00822     gtk_misc_set_alignment(GTK_MISC(label_include), 0, 0.5);
00823     gtk_misc_set_padding(GTK_MISC(label_include), 12, 0);
00824 
00825     filepopup_cover_name_exclude = gtk_entry_new();
00826     gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_exclude, 1, 2, 1, 2,
00827                      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
00828                      (GtkAttachOptions) (0), 0, 0);
00829     gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_exclude), TRUE);
00830 
00831     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00832     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00833     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00834 
00835     filepopup_recurse = gtk_check_button_new_with_mnemonic(_("Recursively search for cover"));
00836     gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse);
00837 
00838     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00839     gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
00840     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 45, 0);
00841 
00842     filepopup_recurse_depth_box = gtk_hbox_new(FALSE, 0);
00843     gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse_depth_box);
00844 
00845     label_search_depth = gtk_label_new(_("Search depth: "));
00846     gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), label_search_depth, TRUE, TRUE, 0);
00847     gtk_misc_set_padding(GTK_MISC(label_search_depth), 4, 0);
00848 
00849     recurse_for_cover_depth_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0,
00850      100, 1, 10, 0);
00851     filepopup_recurse_depth = gtk_spin_button_new(GTK_ADJUSTMENT(recurse_for_cover_depth_adj), 1, 0);
00852     gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), filepopup_recurse_depth, TRUE, TRUE, 0);
00853     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_recurse_depth), TRUE);
00854 
00855     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00856     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00857     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00858 
00859     filepopup_use_file_cover = gtk_check_button_new_with_mnemonic(_("Use per-file cover"));
00860     gtk_container_add(GTK_CONTAINER(alignment), filepopup_use_file_cover);
00861 
00862     label_misc = gtk_label_new(_("<b>Miscellaneous</b>"));
00863     gtk_box_pack_start(GTK_BOX(vbox), label_misc, FALSE, FALSE, 0);
00864     gtk_label_set_use_markup(GTK_LABEL(label_misc), TRUE);
00865     gtk_misc_set_alignment(GTK_MISC(label_misc), 0, 0.5);
00866 
00867     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00868     gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
00869     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00870 
00871     filepopup_showprogressbar = gtk_check_button_new_with_mnemonic(_("Show Progress bar for the current track"));
00872     gtk_container_add(GTK_CONTAINER(alignment), filepopup_showprogressbar);
00873 
00874     alignment = gtk_alignment_new(0, 0.5, 1, 1);
00875     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00876     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00877 
00878     hbox = gtk_hbox_new(FALSE, 0);
00879     gtk_container_add(GTK_CONTAINER(alignment), hbox);
00880 
00881     label_delay = gtk_label_new(_("Delay until filepopup comes up: "));
00882     gtk_box_pack_start(GTK_BOX(hbox), label_delay, TRUE, TRUE, 0);
00883     gtk_misc_set_alignment(GTK_MISC(label_delay), 0, 0.5);
00884     gtk_misc_set_padding(GTK_MISC(label_delay), 12, 0);
00885 
00886     delay_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 100, 1, 10, 0);
00887     filepopup_delay = gtk_spin_button_new(GTK_ADJUSTMENT(delay_adj), 1, 0);
00888     gtk_box_pack_start(GTK_BOX(hbox), filepopup_delay, TRUE, TRUE, 0);
00889     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_delay), TRUE);
00890 
00891     hbuttonbox = gtk_hbutton_box_new();
00892     gtk_box_pack_start(GTK_BOX(vbox), hbuttonbox, FALSE, FALSE, 0);
00893     gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox), GTK_BUTTONBOX_END);
00894     gtk_box_set_spacing(GTK_BOX(hbuttonbox), 6);
00895 
00896     btn_cancel = gtk_button_new_from_stock("gtk-cancel");
00897     gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_cancel);
00898 
00899     btn_ok = gtk_button_new_from_stock("gtk-ok");
00900     gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_ok);
00901     gtk_widget_set_can_default(btn_ok, TRUE);
00902 
00903     g_signal_connect(G_OBJECT(filepopup_settings), "delete_event",
00904                      G_CALLBACK(gtk_widget_hide_on_delete),
00905                      NULL);
00906     g_signal_connect(G_OBJECT(btn_cancel), "clicked",
00907                      G_CALLBACK(on_filepopup_cancel_clicked),
00908                      NULL);
00909     g_signal_connect(G_OBJECT(btn_ok), "clicked",
00910                      G_CALLBACK(on_filepopup_ok_clicked),
00911                      NULL);
00912 
00913     gtk_widget_grab_default(btn_ok);
00914     gtk_widget_show_all(vbox);
00915 }
00916 
00917 static void create_spin_button (PreferencesWidget * widget, GtkWidget * *
00918  label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char *
00919  domain)
00920 {
00921      g_return_if_fail(widget->type == WIDGET_SPIN_BTN);
00922 
00923      * label_pre = gtk_label_new (dgettext (domain, widget->label));
00924 
00925      *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min,
00926                                                 widget->data.spin_btn.max,
00927                                                 widget->data.spin_btn.step);
00928 
00929 
00930      if (widget->tooltip)
00931          gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain,
00932           widget->tooltip));
00933 
00934      if (widget->data.spin_btn.right_label) {
00935          * label_past = gtk_label_new (dgettext (domain,
00936           widget->data.spin_btn.right_label));
00937      }
00938 
00939     switch (widget->cfg_type)
00940     {
00941     case VALUE_INT:
00942         gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
00943         g_signal_connect (* spin_btn, "value_changed", (GCallback) on_spin_btn_changed_int, widget);
00944         break;
00945     case VALUE_FLOAT:
00946         gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
00947         g_signal_connect (* spin_btn, "value_changed", (GCallback)
00948          on_spin_btn_changed_float, widget);
00949         break;
00950     default:
00951         break;
00952     }
00953 }
00954 
00955 void create_font_btn (PreferencesWidget * widget, GtkWidget * * label,
00956  GtkWidget * * font_btn, const char * domain)
00957 {
00958     *font_btn = gtk_font_button_new();
00959     gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE);
00960     gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE);
00961     if (widget->label) {
00962         * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
00963         gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
00964         gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5);
00965         gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
00966         gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn);
00967     }
00968 
00969     if (widget->data.font_btn.title)
00970         gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn),
00971          dgettext (domain, widget->data.font_btn.title));
00972 
00973     char * name = widget_get_string (widget);
00974     if (name)
00975     {
00976         gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
00977         g_free (name);
00978     }
00979 
00980     g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, widget);
00981 }
00982 
00983 static void create_entry (PreferencesWidget * widget, GtkWidget * * label,
00984  GtkWidget * * entry, const char * domain)
00985 {
00986     *entry = gtk_entry_new();
00987     gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password);
00988 
00989     if (widget->label)
00990         * label = gtk_label_new (dgettext (domain, widget->label));
00991 
00992     if (widget->tooltip)
00993         gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
00994 
00995     if (widget->cfg_type == VALUE_STRING)
00996     {
00997         char * value = widget_get_string (widget);
00998         if (value)
00999         {
01000             gtk_entry_set_text ((GtkEntry *) * entry, value);
01001             g_free (value);
01002         }
01003 
01004         g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, widget);
01005     }
01006 }
01007 
01008 static void create_label (PreferencesWidget * widget, GtkWidget * * label,
01009  GtkWidget * * icon, const char * domain)
01010 {
01011     if (widget->data.label.stock_id)
01012         *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
01013 
01014     * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
01015     gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
01016 
01017     if (widget->data.label.single_line == FALSE)
01018         gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE);
01019 
01020     gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
01021 }
01022 
01023 static void create_cbox (PreferencesWidget * widget, GtkWidget * * label,
01024  GtkWidget * * combobox, const char * domain)
01025 {
01026     * combobox = gtk_combo_box_text_new ();
01027 
01028     if (widget->label) {
01029         * label = gtk_label_new (dgettext (domain, widget->label));
01030     }
01031 
01032     fill_cbox (* combobox, widget);
01033 }
01034 
01035 static void fill_table (GtkWidget * table, PreferencesWidget * elements, int
01036  amt, const char * domain)
01037 {
01038     int x;
01039     GtkWidget *widget_left, *widget_middle, *widget_right;
01040     GtkAttachOptions middle_policy = (GtkAttachOptions) (0);
01041 
01042     for (x = 0; x < amt; ++x) {
01043         widget_left = widget_middle = widget_right = NULL;
01044         switch (elements[x].type) {
01045             case WIDGET_SPIN_BTN:
01046                 create_spin_button (& elements[x], & widget_left,
01047                  & widget_middle, & widget_right, domain);
01048                 middle_policy = (GtkAttachOptions) (GTK_FILL);
01049                 break;
01050             case WIDGET_LABEL:
01051                 create_label (& elements[x], & widget_middle, & widget_left,
01052                  domain);
01053                 middle_policy = (GtkAttachOptions) (GTK_FILL);
01054                 break;
01055             case WIDGET_FONT_BTN:
01056                 create_font_btn (& elements[x], & widget_left, & widget_middle,
01057                  domain);
01058                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01059                 break;
01060             case WIDGET_ENTRY:
01061                 create_entry (& elements[x], & widget_left, & widget_middle,
01062                  domain);
01063                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01064                 break;
01065             case WIDGET_COMBO_BOX:
01066                 create_cbox (& elements[x], & widget_left, & widget_middle,
01067                  domain);
01068                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01069                 break;
01070             default:
01071                 g_warning("Unsupported widget type %d in table", elements[x].type);
01072         }
01073 
01074         if (widget_left)
01075             gtk_table_attach(GTK_TABLE (table), widget_left, 0, 1, x, x+1,
01076                              (GtkAttachOptions) (0),
01077                              (GtkAttachOptions) (0), 0, 0);
01078 
01079         if (widget_middle)
01080             gtk_table_attach(GTK_TABLE(table), widget_middle, 1, widget_right ? 2 : 3, x, x+1,
01081                              middle_policy,
01082                              (GtkAttachOptions) (0), 4, 0);
01083 
01084         if (widget_right)
01085             gtk_table_attach(GTK_TABLE(table), widget_right, 2, 3, x, x+1,
01086                              (GtkAttachOptions) (0),
01087                              (GtkAttachOptions) (0), 0, 0);
01088     }
01089 }
01090 
01091 /* void create_widgets_with_domain (GtkBox * box, PreferencesWidget * widgets,
01092  int amt, const char * domain) */
01093 void create_widgets_with_domain (void * box, PreferencesWidget * widgets, int
01094  amt, const char * domain)
01095 {
01096     int x;
01097     GtkWidget *alignment = NULL, *widget = NULL;
01098     GtkWidget *child_box = NULL;
01099     GSList *radio_btn_group = NULL;
01100 
01101     for (x = 0; x < amt; ++x) {
01102         if (widget && widgets[x].child)
01103         {
01104             if (!child_box) {
01105                 child_box = gtk_vbox_new(FALSE, 0);
01106                 g_object_set_data(G_OBJECT(widget), "child", child_box);
01107                 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
01108                 gtk_box_pack_start(box, alignment, FALSE, FALSE, 0);
01109                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
01110                 gtk_container_add (GTK_CONTAINER (alignment), child_box);
01111 
01112                 if (GTK_IS_TOGGLE_BUTTON (widget))
01113                     gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget));
01114             }
01115         } else
01116             child_box = NULL;
01117 
01118         alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
01119         gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
01120         gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0);
01121 
01122         if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN)
01123             radio_btn_group = NULL;
01124 
01125         switch(widgets[x].type) {
01126             case WIDGET_CHK_BTN:
01127                 widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label));
01128                 init_toggle_button (widget, & widgets[x]);
01129                 break;
01130             case WIDGET_LABEL:
01131                 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 12, 0, 0, 0);
01132 
01133                 GtkWidget *label = NULL, *icon = NULL;
01134                 create_label (& widgets[x], & label, & icon, domain);
01135 
01136                 if (icon == NULL)
01137                     widget = label;
01138                 else {
01139                     widget = gtk_hbox_new(FALSE, 6);
01140                     gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0);
01141                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01142                 }
01143                 break;
01144             case WIDGET_RADIO_BTN:
01145                 widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
01146                  dgettext (domain, widgets[x].label));
01147                 radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
01148                 init_toggle_button (widget, & widgets[x]);
01149                 break;
01150             case WIDGET_SPIN_BTN:
01151                 widget = gtk_hbox_new(FALSE, 6);
01152 
01153                 GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL;
01154                 create_spin_button (& widgets[x], & label_pre, & spin_btn,
01155                  & label_past, domain);
01156 
01157                 if (label_pre)
01158                     gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0);
01159                 if (spin_btn)
01160                     gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0);
01161                 if (label_past)
01162                     gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0);
01163 
01164                 break;
01165             case WIDGET_CUSTOM:  /* custom widget. --nenolod */
01166                 if (widgets[x].data.populate)
01167                     widget = widgets[x].data.populate();
01168                 else
01169                     widget = NULL;
01170 
01171                 break;
01172             case WIDGET_FONT_BTN:
01173                 widget = gtk_hbox_new(FALSE, 6);
01174 
01175                 GtkWidget *font_btn = NULL;
01176                 create_font_btn (& widgets[x], & label, & font_btn, domain);
01177 
01178                 if (label)
01179                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01180                 if (font_btn)
01181                     gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0);
01182                 break;
01183             case WIDGET_TABLE:
01184                 widget = gtk_table_new(widgets[x].data.table.rows, 3, FALSE);
01185                 fill_table (widget, widgets[x].data.table.elem,
01186                  widgets[x].data.table.rows, domain);
01187                 gtk_table_set_row_spacings(GTK_TABLE(widget), 6);
01188                 break;
01189             case WIDGET_ENTRY:
01190                 widget = gtk_hbox_new(FALSE, 6);
01191 
01192                 GtkWidget *entry = NULL;
01193                 create_entry (& widgets[x], & label, & entry, domain);
01194 
01195                 if (label)
01196                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01197                 if (entry)
01198                     gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
01199                 break;
01200             case WIDGET_COMBO_BOX:
01201                 widget = gtk_hbox_new(FALSE, 6);
01202 
01203                 GtkWidget *combo = NULL;
01204                 create_cbox (& widgets[x], & label, & combo, domain);
01205 
01206                 if (label)
01207                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01208                 if (combo)
01209                     gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0);
01210                 break;
01211             case WIDGET_BOX:
01212                 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0);
01213 
01214                 if (widgets[x].data.box.horizontal) {
01215                     widget = gtk_hbox_new(FALSE, 0);
01216                 } else {
01217                     widget = gtk_vbox_new(FALSE, 0);
01218                 }
01219 
01220                 create_widgets_with_domain ((GtkBox *) widget,
01221                  widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain);
01222 
01223                 if (widgets[x].data.box.frame) {
01224                     GtkWidget *tmp;
01225                     tmp = widget;
01226 
01227                     widget = gtk_frame_new (dgettext (domain, widgets[x].label));
01228                     gtk_container_add(GTK_CONTAINER(widget), tmp);
01229                 }
01230                 break;
01231             case WIDGET_NOTEBOOK:
01232                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
01233 
01234                 widget = gtk_notebook_new();
01235 
01236                 int i;
01237                 for (i = 0; i<widgets[x].data.notebook.n_tabs; i++) {
01238                     GtkWidget *vbox;
01239                     vbox = gtk_vbox_new(FALSE, 5);
01240                     create_widgets_with_domain ((GtkBox *) vbox,
01241                      widgets[x].data.notebook.tabs[i].settings,
01242                      widgets[x].data.notebook.tabs[i].n_settings, domain);
01243 
01244                     gtk_notebook_append_page (GTK_NOTEBOOK (widget), vbox,
01245                      gtk_label_new (dgettext (domain,
01246                      widgets[x].data.notebook.tabs[i].name)));
01247                 }
01248                 break;
01249             case WIDGET_SEPARATOR:
01250                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 0, 0);
01251 
01252                 if (widgets[x].data.separator.horizontal == TRUE) {
01253                     widget = gtk_hseparator_new();
01254                 } else {
01255                     widget = gtk_vseparator_new();
01256                 }
01257                 break;
01258             default:
01259                 break;
01260         }
01261 
01262         if (widget && !gtk_widget_get_parent(widget))
01263             gtk_container_add(GTK_CONTAINER(alignment), widget);
01264         if (widget && widgets[x].tooltip && widgets[x].type != WIDGET_SPIN_BTN)
01265             gtk_widget_set_tooltip_text (widget, dgettext (domain,
01266              widgets[x].tooltip));
01267     }
01268 
01269 }
01270 
01271 static GtkWidget *
01272 create_titlestring_tag_menu(void)
01273 {
01274     GtkWidget *titlestring_tag_menu, *menu_item;
01275     unsigned int i;
01276 
01277     titlestring_tag_menu = gtk_menu_new();
01278     for(i = 0; i < n_title_field_tags; i++) {
01279         menu_item = gtk_menu_item_new_with_label(_(title_field_tags[i].name));
01280         gtk_menu_shell_append(GTK_MENU_SHELL(titlestring_tag_menu), menu_item);
01281         g_signal_connect(menu_item, "activate",
01282                          G_CALLBACK(titlestring_tag_menu_callback),
01283                          GINT_TO_POINTER(i));
01284     };
01285     gtk_widget_show_all(titlestring_tag_menu);
01286 
01287     return titlestring_tag_menu;
01288 }
01289 
01290 static void show_numbers_cb (GtkToggleButton * numbers, void * unused)
01291 {
01292     set_bool (NULL, "show_numbers_in_pl", gtk_toggle_button_get_active (numbers));
01293 
01294     hook_call ("title change", NULL);
01295 }
01296 
01297 static void leading_zero_cb (GtkToggleButton * leading)
01298 {
01299     set_bool (NULL, "leading_zero", gtk_toggle_button_get_active (leading));
01300 
01301     hook_call ("title change", NULL);
01302 }
01303 
01304 static void create_titlestring_widgets (GtkWidget * * cbox, GtkWidget * * entry)
01305 {
01306     * cbox = gtk_combo_box_text_new ();
01307     for (int i = 0; i < TITLESTRING_NPRESETS; i ++)
01308         gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _(titlestring_preset_names[i]));
01309     gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _("Custom"));
01310 
01311     * entry = gtk_entry_new ();
01312 
01313     char * format = get_string (NULL, "generic_title_format");
01314     update_titlestring_cbox ((GtkComboBox *) * cbox, format);
01315     gtk_entry_set_text ((GtkEntry *) * entry, format);
01316     g_free (format);
01317 
01318     g_signal_connect (* cbox, "changed", (GCallback) on_titlestring_cbox_changed, * entry);
01319     g_signal_connect (* entry, "changed", (GCallback) on_titlestring_entry_changed, * cbox);
01320 }
01321 
01322 static void
01323 create_playlist_category(void)
01324 {
01325     GtkWidget *vbox5;
01326     GtkWidget *alignment55;
01327     GtkWidget *label60;
01328     GtkWidget *alignment56;
01329     GtkWidget *table6;
01330     GtkWidget *titlestring_help_button;
01331     GtkWidget *image1;
01332     GtkWidget *label62;
01333     GtkWidget *label61;
01334     GtkWidget *alignment85;
01335     GtkWidget *label84;
01336     GtkWidget *alignment86;
01337     GtkWidget *hbox9;
01338     GtkWidget *vbox34;
01339     GtkWidget *image8;
01340     GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu();
01341     GtkWidget * numbers_alignment, * numbers;
01342 
01343     vbox5 = gtk_vbox_new (FALSE, 0);
01344     gtk_container_add ((GtkContainer *) category_notebook, vbox5);
01345 
01346     create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets));
01347 
01348     alignment55 = gtk_alignment_new (0.5, 0.5, 1, 1);
01349     gtk_box_pack_start (GTK_BOX (vbox5), alignment55, FALSE, FALSE, 0);
01350     gtk_alignment_set_padding ((GtkAlignment *) alignment55, 12, 3, 0, 0);
01351 
01352     label60 = gtk_label_new (_("<b>Song Display</b>"));
01353     gtk_container_add (GTK_CONTAINER (alignment55), label60);
01354     gtk_label_set_use_markup (GTK_LABEL (label60), TRUE);
01355     gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5);
01356 
01357     numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
01358     gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
01359     gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
01360 
01361     numbers = gtk_check_button_new_with_label (_("Show song numbers"));
01362     gtk_toggle_button_set_active ((GtkToggleButton *) numbers,
01363      get_bool (NULL, "show_numbers_in_pl"));
01364     g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
01365      show_numbers_cb, 0);
01366     gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
01367 
01368     numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
01369     gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
01370     gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
01371 
01372     numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 "
01373      "instead of 2:00)"));
01374     gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero"));
01375     g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
01376      leading_zero_cb, 0);
01377     gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
01378 
01379     alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1);
01380     gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0);
01381     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0);
01382 
01383     table6 = gtk_table_new (2, 3, FALSE);
01384     gtk_container_add (GTK_CONTAINER (alignment56), table6);
01385     gtk_table_set_row_spacings (GTK_TABLE (table6), 4);
01386     gtk_table_set_col_spacings (GTK_TABLE (table6), 12);
01387 
01388     titlestring_help_button = gtk_button_new ();
01389     gtk_table_attach (GTK_TABLE (table6), titlestring_help_button, 2, 3, 1, 2,
01390                       (GtkAttachOptions) (0),
01391                       (GtkAttachOptions) (0), 0, 0);
01392 
01393     gtk_widget_set_can_focus (titlestring_help_button, FALSE);
01394     gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format"));
01395     gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF);
01396     gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE);
01397 
01398     image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON);
01399     gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1);
01400 
01401     GtkWidget * titlestring_cbox;
01402     create_titlestring_widgets (& titlestring_cbox, & titlestring_entry);
01403     gtk_table_attach (GTK_TABLE (table6), titlestring_cbox, 1, 3, 0, 1,
01404                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
01405                       (GtkAttachOptions) (0), 0, 0);
01406     gtk_table_attach (GTK_TABLE (table6), titlestring_entry, 1, 2, 1, 2,
01407                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
01408                       (GtkAttachOptions) (0), 0, 0);
01409 
01410     label62 = gtk_label_new (_("Custom string:"));
01411     gtk_table_attach (GTK_TABLE (table6), label62, 0, 1, 1, 2,
01412                       (GtkAttachOptions) (0),
01413                       (GtkAttachOptions) (0), 0, 0);
01414     gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT);
01415     gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5);
01416 
01417     label61 = gtk_label_new (_("Title format:"));
01418     gtk_table_attach (GTK_TABLE (table6), label61, 0, 1, 0, 1,
01419                       (GtkAttachOptions) (0),
01420                       (GtkAttachOptions) (0), 0, 0);
01421     gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT);
01422     gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5);
01423 
01424     alignment85 = gtk_alignment_new (0.5, 0.5, 1, 1);
01425     gtk_box_pack_start (GTK_BOX (vbox5), alignment85, FALSE, FALSE, 0);
01426     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment85), 12, 12, 0, 0);
01427 
01428     label84 = gtk_label_new (_("<b>Popup Information</b>"));
01429     gtk_container_add (GTK_CONTAINER (alignment85), label84);
01430     gtk_label_set_use_markup (GTK_LABEL (label84), TRUE);
01431     gtk_misc_set_alignment (GTK_MISC (label84), 0, 0.5);
01432 
01433     alignment86 = gtk_alignment_new (0.5, 0.5, 1, 1);
01434     gtk_box_pack_start (GTK_BOX (vbox5), alignment86, FALSE, FALSE, 0);
01435     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment86), 0, 0, 12, 0);
01436 
01437     hbox9 = gtk_hbox_new (FALSE, 12);
01438     gtk_container_add (GTK_CONTAINER (alignment86), hbox9);
01439 
01440     vbox34 = gtk_vbox_new (FALSE, 0);
01441     gtk_box_pack_start (GTK_BOX (hbox9), vbox34, TRUE, TRUE, 0);
01442 
01443     filepopupbutton = gtk_check_button_new_with_mnemonic (_("Show popup information for playlist entries"));
01444     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."));
01445     gtk_toggle_button_set_active ((GtkToggleButton *) filepopupbutton,
01446      get_bool (NULL, "show_filepopup_for_tuple"));
01447     gtk_box_pack_start ((GtkBox *) vbox34, filepopupbutton, TRUE, FALSE, 0);
01448 
01449     filepopup_settings_button = gtk_button_new ();
01450     gtk_widget_set_sensitive (filepopup_settings_button,
01451      get_bool (NULL, "show_filepopup_for_tuple"));
01452     gtk_box_pack_start (GTK_BOX (hbox9), filepopup_settings_button, FALSE, FALSE, 0);
01453 
01454     gtk_widget_set_can_focus (filepopup_settings_button, FALSE);
01455     gtk_widget_set_tooltip_text (filepopup_settings_button, _("Edit settings for popup information"));
01456     gtk_button_set_relief (GTK_BUTTON (filepopup_settings_button), GTK_RELIEF_HALF);
01457 
01458     image8 = gtk_image_new_from_stock ("gtk-properties", GTK_ICON_SIZE_BUTTON);
01459     gtk_container_add (GTK_CONTAINER (filepopup_settings_button), image8);
01460 
01461 
01462 
01463     g_signal_connect (filepopupbutton, "toggled",
01464                      G_CALLBACK(on_show_filepopup_toggled),
01465                      NULL);
01466     g_signal_connect(G_OBJECT(filepopup_settings_button), "clicked",
01467                      G_CALLBACK(on_filepopup_settings_clicked),
01468                      NULL);
01469 
01470     g_signal_connect(titlestring_help_button, "clicked",
01471                      G_CALLBACK(on_titlestring_help_button_clicked),
01472                      titlestring_tag_menu);
01473 
01474     /* Create window for filepopup settings */
01475     create_filepopup_settings();
01476 }
01477 
01478 static GtkWidget * output_config_button, * output_about_button;
01479 
01480 static bool_t output_enum_cb (PluginHandle * plugin, GList * * list)
01481 {
01482     * list = g_list_prepend (* list, plugin);
01483     return TRUE;
01484 }
01485 
01486 static GList * output_get_list (void)
01487 {
01488     static GList * list = NULL;
01489 
01490     if (list == NULL)
01491     {
01492         plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_enum_cb,
01493          & list);
01494         list = g_list_reverse (list);
01495     }
01496 
01497     return list;
01498 }
01499 
01500 static void output_combo_update (GtkComboBox * combo)
01501 {
01502     PluginHandle * plugin = plugin_get_current (PLUGIN_TYPE_OUTPUT);
01503     gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin));
01504     gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
01505     gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
01506 }
01507 
01508 static void output_combo_changed (GtkComboBox * combo)
01509 {
01510     PluginHandle * plugin = g_list_nth_data (output_get_list (),
01511      gtk_combo_box_get_active (combo));
01512     g_return_if_fail (plugin != NULL);
01513 
01514     plugin_enable (plugin, TRUE);
01515     output_combo_update (combo);
01516 }
01517 
01518 static void output_combo_fill (GtkComboBox * combo)
01519 {
01520     for (GList * node = output_get_list (); node != NULL; node = node->next)
01521         gtk_combo_box_text_append_text ((GtkComboBoxText *) combo,
01522          plugin_get_name (node->data));
01523 }
01524 
01525 static void output_do_config (void)
01526 {
01527     OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
01528     g_return_if_fail (op != NULL);
01529     if (op->configure != NULL)
01530         op->configure ();
01531 }
01532 
01533 static void output_do_about (void)
01534 {
01535     OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
01536     g_return_if_fail (op != NULL);
01537     if (op->about != NULL)
01538         op->about ();
01539 }
01540 
01541 static void * create_output_plugin_box (void)
01542 {
01543     GtkWidget * hbox1 = gtk_hbox_new (FALSE, 6);
01544     gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0);
01545 
01546     GtkWidget * vbox = gtk_vbox_new (FALSE, 6);
01547     gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0);
01548 
01549     GtkWidget * hbox2 = gtk_hbox_new (FALSE, 6);
01550     gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0);
01551 
01552     GtkWidget * output_plugin_cbox = gtk_combo_box_text_new ();
01553     gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0);
01554 
01555     GtkWidget * hbox3 = gtk_hbox_new (FALSE, 6);
01556     gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0);
01557 
01558     output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
01559     gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0);
01560 
01561     output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
01562     gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0);
01563 
01564     output_combo_fill ((GtkComboBox *) output_plugin_cbox);
01565     output_combo_update ((GtkComboBox *) output_plugin_cbox);
01566 
01567     g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL);
01568     g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL);
01569     g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL);
01570 
01571     return hbox1;
01572 }
01573 
01574 static void create_audio_category (void)
01575 {
01576     GtkWidget * audio_page_vbox = gtk_vbox_new (FALSE, 0);
01577     create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets));
01578     gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox);
01579 }
01580 
01581 static void
01582 create_connectivity_category(void)
01583 {
01584     GtkWidget *connectivity_page_vbox;
01585     GtkWidget *vbox29;
01586 
01587     connectivity_page_vbox = gtk_vbox_new (FALSE, 0);
01588     gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox);
01589 
01590     vbox29 = gtk_vbox_new (FALSE, 0);
01591     gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0);
01592 
01593     create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets));
01594 }
01595 
01596 static void create_plugin_category (void)
01597 {
01598     GtkWidget * notebook = gtk_notebook_new ();
01599     gtk_container_add ((GtkContainer *) category_notebook, notebook);
01600 
01601     int types[] = {PLUGIN_TYPE_TRANSPORT, PLUGIN_TYPE_PLAYLIST,
01602      PLUGIN_TYPE_INPUT, PLUGIN_TYPE_EFFECT, PLUGIN_TYPE_VIS, PLUGIN_TYPE_GENERAL};
01603     const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"),
01604      N_("Effect"), N_("Visualization"), N_("General")};
01605 
01606     for (int i = 0; i < G_N_ELEMENTS (types); i ++)
01607         gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new
01608          (types[i]), gtk_label_new (_(names[i])));
01609 }
01610 
01611 static bool_t
01612 prefswin_destroy(GtkWidget *window, GdkEvent *event, gpointer data)
01613 {
01614     prefswin = NULL;
01615     category_notebook = NULL;
01616     gtk_widget_destroy(filepopup_settings);
01617     filepopup_settings = NULL;
01618     gtk_widget_destroy(window);
01619     return TRUE;
01620 }
01621 
01622 /* GtkWidget * * create_prefs_window (void) */
01623 void * * create_prefs_window (void)
01624 {
01625     char *aud_version_string;
01626 
01627     GtkWidget *vbox;
01628     GtkWidget *hbox1;
01629     GtkWidget *scrolledwindow6;
01630     GtkWidget *hseparator1;
01631     GtkWidget *hbox4;
01632     GtkWidget *audversionlabel;
01633     GtkWidget *prefswin_button_box;
01634     GtkWidget *hbox11;
01635     GtkWidget *image10;
01636     GtkWidget *close;
01637     GtkAccelGroup *accel_group;
01638 
01639     accel_group = gtk_accel_group_new ();
01640 
01641     prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
01642     gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG);
01643     gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12);
01644     gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences"));
01645     gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER);
01646     gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400);
01647 
01648     vbox = gtk_vbox_new (FALSE, 0);
01649     gtk_container_add (GTK_CONTAINER (prefswin), vbox);
01650 
01651     hbox1 = gtk_hbox_new (FALSE, 8);
01652     gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);
01653 
01654     scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL);
01655     gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0);
01656     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
01657     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN);
01658 
01659     category_treeview = gtk_tree_view_new ();
01660     gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview);
01661     gtk_widget_set_size_request (scrolledwindow6, 168, -1);
01662     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE);
01663 
01664     category_notebook = gtk_notebook_new ();
01665     gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0);
01666 
01667     gtk_widget_set_can_focus (category_notebook, FALSE);
01668     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE);
01669     gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE);
01670     gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE);
01671 
01672     create_audio_category();
01673     create_connectivity_category();
01674     create_playlist_category();
01675     create_plugin_category();
01676 
01677     hseparator1 = gtk_hseparator_new ();
01678     gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6);
01679 
01680     hbox4 = gtk_hbox_new (FALSE, 0);
01681     gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0);
01682 
01683     audversionlabel = gtk_label_new ("");
01684     gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0);
01685     gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE);
01686 
01687     prefswin_button_box = gtk_hbutton_box_new ();
01688     gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0);
01689     gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END);
01690     gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6);
01691 
01692     hbox11 = gtk_hbox_new (FALSE, 2);
01693 
01694     image10 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
01695     gtk_box_pack_start (GTK_BOX (hbox11), image10, FALSE, FALSE, 0);
01696 
01697     close = gtk_button_new_from_stock ("gtk-close");
01698     gtk_container_add (GTK_CONTAINER (prefswin_button_box), close);
01699     gtk_widget_set_can_default(close, TRUE);
01700     gtk_widget_add_accelerator (close, "clicked", accel_group,
01701                                 GDK_Escape, (GdkModifierType) 0,
01702                                 GTK_ACCEL_VISIBLE);
01703 
01704 
01705     gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group);
01706 
01707     /* connect signals */
01708     g_signal_connect(G_OBJECT(prefswin), "delete_event",
01709                      G_CALLBACK(prefswin_destroy),
01710                      NULL);
01711     g_signal_connect_swapped(G_OBJECT(close), "clicked",
01712                              G_CALLBACK(prefswin_destroy),
01713                              prefswin);
01714 
01715     /* create category view */
01716     fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook);
01717 
01718     /* audacious version label */
01719 
01720     aud_version_string = g_strdup_printf
01721      ("<span size='small'>%s (%s)</span>", "Audacious " VERSION, BUILDSTAMP);
01722 
01723     gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string );
01724     g_free(aud_version_string);
01725     gtk_widget_show_all(vbox);
01726 
01727     return & prefswin;
01728 }
01729 
01730 void
01731 destroy_prefs_window(void)
01732 {
01733     prefswin_destroy(prefswin, NULL, NULL);
01734 }
01735 
01736 void show_prefs_window (void)
01737 {
01738     if (! prefswin)
01739         create_prefs_window ();
01740 
01741     gtk_window_present ((GtkWindow *) prefswin);
01742 }
01743 
01744 void
01745 hide_prefs_window(void)
01746 {
01747     g_return_if_fail(prefswin);
01748     gtk_widget_hide(GTK_WIDGET(prefswin));
01749 }
01750 
01751 static void prefswin_page_queue_new (GtkWidget * container, const char * name,
01752  const char * imgurl)
01753 {
01754     CategoryQueueEntry *ent = g_new0(CategoryQueueEntry, 1);
01755 
01756     ent->container = container;
01757     ent->pg_name = name;
01758     ent->img_url = imgurl;
01759 
01760     if (category_queue)
01761         ent->next = category_queue;
01762 
01763     category_queue = ent;
01764 }
01765 
01766 static void
01767 prefswin_page_queue_destroy(CategoryQueueEntry *ent)
01768 {
01769     category_queue = ent->next;
01770     g_free(ent);
01771 }
01772 
01773 /*
01774  * Public APIs for adding new pages to the prefs window.
01775  *
01776  * Basically, the concept here is that third party components can register themselves in the root
01777  * preferences window.
01778  *
01779  * From a usability standpoint this makes the application look more "united", instead of cluttered
01780  * and malorganised. Hopefully this option will be used further in the future.
01781  *
01782  *    - nenolod
01783  */
01784 /* int prefswin_page_new (GtkWidget * container, const char * name,
01785  const char * imgurl) */
01786 int prefswin_page_new (void * container, const char * name, const char *
01787  imgurl)
01788 {
01789     GtkTreeModel *model;
01790     GtkTreeIter iter;
01791     GdkPixbuf *img = NULL;
01792     GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
01793     int id;
01794 
01795     if (treeview == NULL || category_notebook == NULL)
01796     {
01797         prefswin_page_queue_new(container, name, imgurl);
01798         return -1;
01799     }
01800 
01801     model = gtk_tree_view_get_model(treeview);
01802 
01803     if (model == NULL)
01804     {
01805         prefswin_page_queue_new(container, name, imgurl);
01806         return -1;
01807     }
01808 
01809     /* Make sure the widgets are visible. */
01810     gtk_widget_show(container);
01811     id = gtk_notebook_append_page(GTK_NOTEBOOK(category_notebook), container, NULL);
01812 
01813     if (id == -1)
01814         return -1;
01815 
01816     if (imgurl != NULL)
01817         img = gdk_pixbuf_new_from_file(imgurl, NULL);
01818 
01819     gtk_list_store_append(GTK_LIST_STORE(model), &iter);
01820     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
01821                        CATEGORY_VIEW_COL_ICON, img,
01822                        CATEGORY_VIEW_COL_NAME,
01823                        name, CATEGORY_VIEW_COL_ID, id, -1);
01824 
01825     if (img != NULL)
01826         g_object_unref(img);
01827 
01828     return id;
01829 }
01830 
01831 void
01832 prefswin_page_destroy(GtkWidget *container)
01833 {
01834     GtkTreeModel *model;
01835     GtkTreeIter iter;
01836     GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
01837     bool_t ret;
01838     int id;
01839     int index = -1;
01840 
01841     if (category_notebook == NULL || treeview == NULL || container == NULL)
01842         return;
01843 
01844     id = gtk_notebook_page_num(GTK_NOTEBOOK(category_notebook), container);
01845 
01846     if (id == -1)
01847         return;
01848 
01849     gtk_notebook_remove_page(GTK_NOTEBOOK(category_notebook), id);
01850 
01851     model = gtk_tree_view_get_model(treeview);
01852 
01853     if (model == NULL)
01854         return;
01855 
01856     ret = gtk_tree_model_get_iter_first(model, &iter);
01857 
01858     while (ret == TRUE)
01859     {
01860         gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
01861 
01862         if (index == id)
01863         {
01864             gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
01865             ret = gtk_tree_model_get_iter_first(model, &iter);
01866             continue;
01867         }
01868 
01869         if (index > id)
01870         {
01871             index--;
01872             gtk_list_store_set(GTK_LIST_STORE(model), &iter, CATEGORY_VIEW_COL_ID, index, -1);
01873         }
01874 
01875         ret = gtk_tree_model_iter_next(model, &iter);
01876     }
01877 }
01878 
01879 static void sw_volume_toggled (void)
01880 {
01881     int vol[2];
01882 
01883     if (get_bool (NULL, "software_volume_control"))
01884     {
01885         vol[0] = get_int (NULL, "sw_volume_left");
01886         vol[1] = get_int (NULL, "sw_volume_right");
01887     }
01888     else
01889         playback_get_volume (& vol[0], & vol[1]);
01890 
01891     hook_call ("volume set", vol);
01892 }