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, const char * domain)
00706 {
00707     for (int i = 0; i < widget->data.combo.n_elements; i ++)
00708         gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox,
00709          dgettext (domain, widget->data.combo.elements[i].label));
00710 
00711     switch (widget->cfg_type)
00712     {
00713     case VALUE_INT:;
00714         int ivalue = widget_get_int (widget);
00715 
00716         for (int i = 0; i < widget->data.combo.n_elements; i++)
00717         {
00718             if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue)
00719             {
00720                 gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
00721                 break;
00722             }
00723         }
00724 
00725         g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget);
00726         break;
00727 
00728     case VALUE_STRING:;
00729         char * value = widget_get_string (widget);
00730 
00731         for(int i = 0; i < widget->data.combo.n_elements; i++)
00732         {
00733             if (value && ! strcmp (widget->data.combo.elements[i].value, value))
00734             {
00735                 gtk_combo_box_set_active ((GtkComboBox *) combobox, i);
00736                 break;
00737             }
00738         }
00739 
00740         g_free (value);
00741 
00742         g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget);
00743         break;
00744 
00745     default:
00746         break;
00747     }
00748 }
00749 
00750 void
00751 create_filepopup_settings(void)
00752 {
00753     GtkWidget *vbox;
00754     GtkWidget *table;
00755 
00756     GtkWidget *label_cover_retrieve;
00757     GtkWidget *label_cover_search;
00758     GtkWidget *label_exclude;
00759     GtkWidget *label_include;
00760     GtkWidget *label_search_depth;
00761     GtkWidget *label_misc;
00762     GtkWidget *label_delay;
00763 
00764     GtkAdjustment *recurse_for_cover_depth_adj;
00765     GtkAdjustment *delay_adj;
00766     GtkWidget *alignment;
00767 
00768     GtkWidget *hbox;
00769     GtkWidget *hbuttonbox;
00770     GtkWidget *btn_cancel;
00771     GtkWidget *btn_ok;
00772 
00773     filepopup_settings = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00774     gtk_container_set_border_width(GTK_CONTAINER(filepopup_settings), 12);
00775     gtk_window_set_title(GTK_WINDOW(filepopup_settings), _("Popup Information Settings"));
00776     gtk_window_set_position(GTK_WINDOW(filepopup_settings), GTK_WIN_POS_CENTER_ON_PARENT);
00777     gtk_window_set_skip_taskbar_hint(GTK_WINDOW(filepopup_settings), TRUE);
00778     gtk_window_set_type_hint(GTK_WINDOW(filepopup_settings), GDK_WINDOW_TYPE_HINT_DIALOG);
00779     gtk_window_set_transient_for(GTK_WINDOW(filepopup_settings), GTK_WINDOW(prefswin));
00780 
00781     vbox = gtk_vbox_new(FALSE, 12);
00782     gtk_container_add(GTK_CONTAINER(filepopup_settings), vbox);
00783 
00784     label_cover_retrieve = gtk_label_new(_("<b>Cover image retrieve</b>"));
00785     gtk_box_pack_start(GTK_BOX(vbox), label_cover_retrieve, FALSE, FALSE, 0);
00786     gtk_label_set_use_markup(GTK_LABEL(label_cover_retrieve), TRUE);
00787     gtk_misc_set_alignment(GTK_MISC(label_cover_retrieve), 0, 0.5);
00788 
00789     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."));
00790     gtk_box_pack_start(GTK_BOX(vbox), label_cover_search, FALSE, FALSE, 0);
00791     gtk_label_set_line_wrap(GTK_LABEL(label_cover_search), TRUE);
00792     gtk_misc_set_alignment(GTK_MISC(label_cover_search), 0, 0);
00793     gtk_misc_set_padding(GTK_MISC(label_cover_search), 12, 0);
00794 
00795     table = gtk_table_new(2, 2, FALSE);
00796     gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
00797     gtk_table_set_row_spacings(GTK_TABLE(table), 4);
00798     gtk_table_set_col_spacings(GTK_TABLE(table), 4);
00799 
00800     filepopup_cover_name_include = gtk_entry_new();
00801     gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_include, 1, 2, 0, 1,
00802                      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
00803                      (GtkAttachOptions) (0), 0, 0);
00804     gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_include), TRUE);
00805 
00806     label_exclude = gtk_label_new(_("Exclude:"));
00807     gtk_table_attach(GTK_TABLE(table), label_exclude, 0, 1, 1, 2,
00808                      (GtkAttachOptions) (0),
00809                      (GtkAttachOptions) (0), 0, 0);
00810     gtk_misc_set_alignment(GTK_MISC(label_exclude), 0, 0.5);
00811     gtk_misc_set_padding(GTK_MISC(label_exclude), 12, 0);
00812 
00813     label_include = gtk_label_new(_("Include:"));
00814     gtk_table_attach(GTK_TABLE(table), label_include, 0, 1, 0, 1,
00815                      (GtkAttachOptions) (0),
00816                      (GtkAttachOptions) (0), 0, 0);
00817     gtk_misc_set_alignment(GTK_MISC(label_include), 0, 0.5);
00818     gtk_misc_set_padding(GTK_MISC(label_include), 12, 0);
00819 
00820     filepopup_cover_name_exclude = gtk_entry_new();
00821     gtk_table_attach(GTK_TABLE(table), filepopup_cover_name_exclude, 1, 2, 1, 2,
00822                      (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
00823                      (GtkAttachOptions) (0), 0, 0);
00824     gtk_entry_set_activates_default(GTK_ENTRY(filepopup_cover_name_exclude), TRUE);
00825 
00826     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00827     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00828     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00829 
00830     filepopup_recurse = gtk_check_button_new_with_mnemonic(_("Recursively search for cover"));
00831     gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse);
00832 
00833     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00834     gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
00835     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 45, 0);
00836 
00837     filepopup_recurse_depth_box = gtk_hbox_new(FALSE, 0);
00838     gtk_container_add(GTK_CONTAINER(alignment), filepopup_recurse_depth_box);
00839 
00840     label_search_depth = gtk_label_new(_("Search depth: "));
00841     gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), label_search_depth, TRUE, TRUE, 0);
00842     gtk_misc_set_padding(GTK_MISC(label_search_depth), 4, 0);
00843 
00844     recurse_for_cover_depth_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0,
00845      100, 1, 10, 0);
00846     filepopup_recurse_depth = gtk_spin_button_new(GTK_ADJUSTMENT(recurse_for_cover_depth_adj), 1, 0);
00847     gtk_box_pack_start(GTK_BOX(filepopup_recurse_depth_box), filepopup_recurse_depth, TRUE, TRUE, 0);
00848     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_recurse_depth), TRUE);
00849 
00850     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00851     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00852     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00853 
00854     filepopup_use_file_cover = gtk_check_button_new_with_mnemonic(_("Use per-file cover"));
00855     gtk_container_add(GTK_CONTAINER(alignment), filepopup_use_file_cover);
00856 
00857     label_misc = gtk_label_new(_("<b>Miscellaneous</b>"));
00858     gtk_box_pack_start(GTK_BOX(vbox), label_misc, FALSE, FALSE, 0);
00859     gtk_label_set_use_markup(GTK_LABEL(label_misc), TRUE);
00860     gtk_misc_set_alignment(GTK_MISC(label_misc), 0, 0.5);
00861 
00862     alignment = gtk_alignment_new(0.5, 0.5, 1, 1);
00863     gtk_box_pack_start(GTK_BOX(vbox), alignment, FALSE, FALSE, 0);
00864     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00865 
00866     filepopup_showprogressbar = gtk_check_button_new_with_mnemonic(_("Show Progress bar for the current track"));
00867     gtk_container_add(GTK_CONTAINER(alignment), filepopup_showprogressbar);
00868 
00869     alignment = gtk_alignment_new(0, 0.5, 1, 1);
00870     gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
00871     gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 12, 0);
00872 
00873     hbox = gtk_hbox_new(FALSE, 0);
00874     gtk_container_add(GTK_CONTAINER(alignment), hbox);
00875 
00876     label_delay = gtk_label_new(_("Delay until filepopup comes up: "));
00877     gtk_box_pack_start(GTK_BOX(hbox), label_delay, TRUE, TRUE, 0);
00878     gtk_misc_set_alignment(GTK_MISC(label_delay), 0, 0.5);
00879     gtk_misc_set_padding(GTK_MISC(label_delay), 12, 0);
00880 
00881     delay_adj = (GtkAdjustment *) gtk_adjustment_new (0, 0, 100, 1, 10, 0);
00882     filepopup_delay = gtk_spin_button_new(GTK_ADJUSTMENT(delay_adj), 1, 0);
00883     gtk_box_pack_start(GTK_BOX(hbox), filepopup_delay, TRUE, TRUE, 0);
00884     gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(filepopup_delay), TRUE);
00885 
00886     hbuttonbox = gtk_hbutton_box_new();
00887     gtk_box_pack_start(GTK_BOX(vbox), hbuttonbox, FALSE, FALSE, 0);
00888     gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox), GTK_BUTTONBOX_END);
00889     gtk_box_set_spacing(GTK_BOX(hbuttonbox), 6);
00890 
00891     btn_cancel = gtk_button_new_from_stock("gtk-cancel");
00892     gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_cancel);
00893 
00894     btn_ok = gtk_button_new_from_stock("gtk-ok");
00895     gtk_container_add(GTK_CONTAINER(hbuttonbox), btn_ok);
00896     gtk_widget_set_can_default(btn_ok, TRUE);
00897 
00898     g_signal_connect(G_OBJECT(filepopup_settings), "delete_event",
00899                      G_CALLBACK(gtk_widget_hide_on_delete),
00900                      NULL);
00901     g_signal_connect(G_OBJECT(btn_cancel), "clicked",
00902                      G_CALLBACK(on_filepopup_cancel_clicked),
00903                      NULL);
00904     g_signal_connect(G_OBJECT(btn_ok), "clicked",
00905                      G_CALLBACK(on_filepopup_ok_clicked),
00906                      NULL);
00907 
00908     gtk_widget_grab_default(btn_ok);
00909     gtk_widget_show_all(vbox);
00910 }
00911 
00912 static void create_spin_button (PreferencesWidget * widget, GtkWidget * *
00913  label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char *
00914  domain)
00915 {
00916      g_return_if_fail(widget->type == WIDGET_SPIN_BTN);
00917 
00918      * label_pre = gtk_label_new (dgettext (domain, widget->label));
00919 
00920      *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min,
00921                                                 widget->data.spin_btn.max,
00922                                                 widget->data.spin_btn.step);
00923 
00924 
00925      if (widget->tooltip)
00926          gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain,
00927           widget->tooltip));
00928 
00929      if (widget->data.spin_btn.right_label) {
00930          * label_past = gtk_label_new (dgettext (domain,
00931           widget->data.spin_btn.right_label));
00932      }
00933 
00934     switch (widget->cfg_type)
00935     {
00936     case VALUE_INT:
00937         gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget));
00938         g_signal_connect (* spin_btn, "value_changed", (GCallback) on_spin_btn_changed_int, widget);
00939         break;
00940     case VALUE_FLOAT:
00941         gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget));
00942         g_signal_connect (* spin_btn, "value_changed", (GCallback)
00943          on_spin_btn_changed_float, widget);
00944         break;
00945     default:
00946         break;
00947     }
00948 }
00949 
00950 void create_font_btn (PreferencesWidget * widget, GtkWidget * * label,
00951  GtkWidget * * font_btn, const char * domain)
00952 {
00953     *font_btn = gtk_font_button_new();
00954     gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE);
00955     gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE);
00956     if (widget->label) {
00957         * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
00958         gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
00959         gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5);
00960         gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT);
00961         gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn);
00962     }
00963 
00964     if (widget->data.font_btn.title)
00965         gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn),
00966          dgettext (domain, widget->data.font_btn.title));
00967 
00968     char * name = widget_get_string (widget);
00969     if (name)
00970     {
00971         gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name);
00972         g_free (name);
00973     }
00974 
00975     g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, widget);
00976 }
00977 
00978 static void create_entry (PreferencesWidget * widget, GtkWidget * * label,
00979  GtkWidget * * entry, const char * domain)
00980 {
00981     *entry = gtk_entry_new();
00982     gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password);
00983 
00984     if (widget->label)
00985         * label = gtk_label_new (dgettext (domain, widget->label));
00986 
00987     if (widget->tooltip)
00988         gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip));
00989 
00990     if (widget->cfg_type == VALUE_STRING)
00991     {
00992         char * value = widget_get_string (widget);
00993         if (value)
00994         {
00995             gtk_entry_set_text ((GtkEntry *) * entry, value);
00996             g_free (value);
00997         }
00998 
00999         g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, widget);
01000     }
01001 }
01002 
01003 static void create_label (PreferencesWidget * widget, GtkWidget * * label,
01004  GtkWidget * * icon, const char * domain)
01005 {
01006     if (widget->data.label.stock_id)
01007         *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON);
01008 
01009     * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label));
01010     gtk_label_set_use_markup(GTK_LABEL(*label), TRUE);
01011 
01012     if (widget->data.label.single_line == FALSE)
01013         gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE);
01014 
01015     gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5);
01016 }
01017 
01018 static void create_cbox (PreferencesWidget * widget, GtkWidget * * label,
01019  GtkWidget * * combobox, const char * domain)
01020 {
01021     * combobox = gtk_combo_box_text_new ();
01022 
01023     if (widget->label) {
01024         * label = gtk_label_new (dgettext (domain, widget->label));
01025     }
01026 
01027     fill_cbox (* combobox, widget, domain);
01028 }
01029 
01030 static void fill_table (GtkWidget * table, PreferencesWidget * elements, int
01031  amt, const char * domain)
01032 {
01033     int x;
01034     GtkWidget *widget_left, *widget_middle, *widget_right;
01035     GtkAttachOptions middle_policy = (GtkAttachOptions) (0);
01036 
01037     for (x = 0; x < amt; ++x) {
01038         widget_left = widget_middle = widget_right = NULL;
01039         switch (elements[x].type) {
01040             case WIDGET_SPIN_BTN:
01041                 create_spin_button (& elements[x], & widget_left,
01042                  & widget_middle, & widget_right, domain);
01043                 middle_policy = (GtkAttachOptions) (GTK_FILL);
01044                 break;
01045             case WIDGET_LABEL:
01046                 create_label (& elements[x], & widget_middle, & widget_left,
01047                  domain);
01048                 middle_policy = (GtkAttachOptions) (GTK_FILL);
01049                 break;
01050             case WIDGET_FONT_BTN:
01051                 create_font_btn (& elements[x], & widget_left, & widget_middle,
01052                  domain);
01053                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01054                 break;
01055             case WIDGET_ENTRY:
01056                 create_entry (& elements[x], & widget_left, & widget_middle,
01057                  domain);
01058                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01059                 break;
01060             case WIDGET_COMBO_BOX:
01061                 create_cbox (& elements[x], & widget_left, & widget_middle,
01062                  domain);
01063                 middle_policy = (GtkAttachOptions) (GTK_EXPAND | GTK_FILL);
01064                 break;
01065             default:
01066                 g_warning("Unsupported widget type %d in table", elements[x].type);
01067         }
01068 
01069         if (widget_left)
01070             gtk_table_attach(GTK_TABLE (table), widget_left, 0, 1, x, x+1,
01071                              (GtkAttachOptions) (0),
01072                              (GtkAttachOptions) (0), 0, 0);
01073 
01074         if (widget_middle)
01075             gtk_table_attach(GTK_TABLE(table), widget_middle, 1, widget_right ? 2 : 3, x, x+1,
01076                              middle_policy,
01077                              (GtkAttachOptions) (0), 4, 0);
01078 
01079         if (widget_right)
01080             gtk_table_attach(GTK_TABLE(table), widget_right, 2, 3, x, x+1,
01081                              (GtkAttachOptions) (0),
01082                              (GtkAttachOptions) (0), 0, 0);
01083     }
01084 }
01085 
01086 /* void create_widgets_with_domain (GtkBox * box, PreferencesWidget * widgets,
01087  int amt, const char * domain) */
01088 void create_widgets_with_domain (void * box, PreferencesWidget * widgets, int
01089  amt, const char * domain)
01090 {
01091     int x;
01092     GtkWidget *alignment = NULL, *widget = NULL;
01093     GtkWidget *child_box = NULL;
01094     GSList *radio_btn_group = NULL;
01095 
01096     for (x = 0; x < amt; ++x) {
01097         if (widget && widgets[x].child)
01098         {
01099             if (!child_box) {
01100                 child_box = gtk_vbox_new(FALSE, 0);
01101                 g_object_set_data(G_OBJECT(widget), "child", child_box);
01102                 alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
01103                 gtk_box_pack_start(box, alignment, FALSE, FALSE, 0);
01104                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
01105                 gtk_container_add (GTK_CONTAINER (alignment), child_box);
01106 
01107                 if (GTK_IS_TOGGLE_BUTTON (widget))
01108                     gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget));
01109             }
01110         } else
01111             child_box = NULL;
01112 
01113         alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
01114         gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0);
01115         gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0);
01116 
01117         if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN)
01118             radio_btn_group = NULL;
01119 
01120         switch(widgets[x].type) {
01121             case WIDGET_CHK_BTN:
01122                 widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label));
01123                 init_toggle_button (widget, & widgets[x]);
01124                 break;
01125             case WIDGET_LABEL:
01126                 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 12, 0, 0, 0);
01127 
01128                 GtkWidget *label = NULL, *icon = NULL;
01129                 create_label (& widgets[x], & label, & icon, domain);
01130 
01131                 if (icon == NULL)
01132                     widget = label;
01133                 else {
01134                     widget = gtk_hbox_new(FALSE, 6);
01135                     gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0);
01136                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01137                 }
01138                 break;
01139             case WIDGET_RADIO_BTN:
01140                 widget = gtk_radio_button_new_with_mnemonic (radio_btn_group,
01141                  dgettext (domain, widgets[x].label));
01142                 radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget);
01143                 init_toggle_button (widget, & widgets[x]);
01144                 break;
01145             case WIDGET_SPIN_BTN:
01146                 widget = gtk_hbox_new(FALSE, 6);
01147 
01148                 GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL;
01149                 create_spin_button (& widgets[x], & label_pre, & spin_btn,
01150                  & label_past, domain);
01151 
01152                 if (label_pre)
01153                     gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0);
01154                 if (spin_btn)
01155                     gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0);
01156                 if (label_past)
01157                     gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0);
01158 
01159                 break;
01160             case WIDGET_CUSTOM:  /* custom widget. --nenolod */
01161                 if (widgets[x].data.populate)
01162                     widget = widgets[x].data.populate();
01163                 else
01164                     widget = NULL;
01165 
01166                 break;
01167             case WIDGET_FONT_BTN:
01168                 widget = gtk_hbox_new(FALSE, 6);
01169 
01170                 GtkWidget *font_btn = NULL;
01171                 create_font_btn (& widgets[x], & label, & font_btn, domain);
01172 
01173                 if (label)
01174                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01175                 if (font_btn)
01176                     gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0);
01177                 break;
01178             case WIDGET_TABLE:
01179                 widget = gtk_table_new(widgets[x].data.table.rows, 3, FALSE);
01180                 fill_table (widget, widgets[x].data.table.elem,
01181                  widgets[x].data.table.rows, domain);
01182                 gtk_table_set_row_spacings(GTK_TABLE(widget), 6);
01183                 break;
01184             case WIDGET_ENTRY:
01185                 widget = gtk_hbox_new(FALSE, 6);
01186 
01187                 GtkWidget *entry = NULL;
01188                 create_entry (& widgets[x], & label, & entry, domain);
01189 
01190                 if (label)
01191                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01192                 if (entry)
01193                     gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
01194                 break;
01195             case WIDGET_COMBO_BOX:
01196                 widget = gtk_hbox_new(FALSE, 6);
01197 
01198                 GtkWidget *combo = NULL;
01199                 create_cbox (& widgets[x], & label, & combo, domain);
01200 
01201                 if (label)
01202                     gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0);
01203                 if (combo)
01204                     gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0);
01205                 break;
01206             case WIDGET_BOX:
01207                 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0);
01208 
01209                 if (widgets[x].data.box.horizontal) {
01210                     widget = gtk_hbox_new(FALSE, 0);
01211                 } else {
01212                     widget = gtk_vbox_new(FALSE, 0);
01213                 }
01214 
01215                 create_widgets_with_domain ((GtkBox *) widget,
01216                  widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain);
01217 
01218                 if (widgets[x].data.box.frame) {
01219                     GtkWidget *tmp;
01220                     tmp = widget;
01221 
01222                     widget = gtk_frame_new (dgettext (domain, widgets[x].label));
01223                     gtk_container_add(GTK_CONTAINER(widget), tmp);
01224                 }
01225                 break;
01226             case WIDGET_NOTEBOOK:
01227                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0);
01228 
01229                 widget = gtk_notebook_new();
01230 
01231                 int i;
01232                 for (i = 0; i<widgets[x].data.notebook.n_tabs; i++) {
01233                     GtkWidget *vbox;
01234                     vbox = gtk_vbox_new(FALSE, 5);
01235                     create_widgets_with_domain ((GtkBox *) vbox,
01236                      widgets[x].data.notebook.tabs[i].settings,
01237                      widgets[x].data.notebook.tabs[i].n_settings, domain);
01238 
01239                     gtk_notebook_append_page (GTK_NOTEBOOK (widget), vbox,
01240                      gtk_label_new (dgettext (domain,
01241                      widgets[x].data.notebook.tabs[i].name)));
01242                 }
01243                 break;
01244             case WIDGET_SEPARATOR:
01245                 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 6, 0, 0);
01246 
01247                 if (widgets[x].data.separator.horizontal == TRUE) {
01248                     widget = gtk_hseparator_new();
01249                 } else {
01250                     widget = gtk_vseparator_new();
01251                 }
01252                 break;
01253             default:
01254                 break;
01255         }
01256 
01257         if (widget && !gtk_widget_get_parent(widget))
01258             gtk_container_add(GTK_CONTAINER(alignment), widget);
01259         if (widget && widgets[x].tooltip && widgets[x].type != WIDGET_SPIN_BTN)
01260             gtk_widget_set_tooltip_text (widget, dgettext (domain,
01261              widgets[x].tooltip));
01262     }
01263 
01264 }
01265 
01266 static GtkWidget *
01267 create_titlestring_tag_menu(void)
01268 {
01269     GtkWidget *titlestring_tag_menu, *menu_item;
01270     unsigned int i;
01271 
01272     titlestring_tag_menu = gtk_menu_new();
01273     for(i = 0; i < n_title_field_tags; i++) {
01274         menu_item = gtk_menu_item_new_with_label(_(title_field_tags[i].name));
01275         gtk_menu_shell_append(GTK_MENU_SHELL(titlestring_tag_menu), menu_item);
01276         g_signal_connect(menu_item, "activate",
01277                          G_CALLBACK(titlestring_tag_menu_callback),
01278                          GINT_TO_POINTER(i));
01279     };
01280     gtk_widget_show_all(titlestring_tag_menu);
01281 
01282     return titlestring_tag_menu;
01283 }
01284 
01285 static void show_numbers_cb (GtkToggleButton * numbers, void * unused)
01286 {
01287     set_bool (NULL, "show_numbers_in_pl", gtk_toggle_button_get_active (numbers));
01288     playlist_reformat_titles ();
01289     hook_call ("title change", NULL);
01290 }
01291 
01292 static void leading_zero_cb (GtkToggleButton * leading)
01293 {
01294     set_bool (NULL, "leading_zero", gtk_toggle_button_get_active (leading));
01295     playlist_reformat_titles ();
01296     hook_call ("title change", NULL);
01297 }
01298 
01299 static void create_titlestring_widgets (GtkWidget * * cbox, GtkWidget * * entry)
01300 {
01301     * cbox = gtk_combo_box_text_new ();
01302     for (int i = 0; i < TITLESTRING_NPRESETS; i ++)
01303         gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _(titlestring_preset_names[i]));
01304     gtk_combo_box_text_append_text ((GtkComboBoxText *) * cbox, _("Custom"));
01305 
01306     * entry = gtk_entry_new ();
01307 
01308     char * format = get_string (NULL, "generic_title_format");
01309     update_titlestring_cbox ((GtkComboBox *) * cbox, format);
01310     gtk_entry_set_text ((GtkEntry *) * entry, format);
01311     g_free (format);
01312 
01313     g_signal_connect (* cbox, "changed", (GCallback) on_titlestring_cbox_changed, * entry);
01314     g_signal_connect (* entry, "changed", (GCallback) on_titlestring_entry_changed, * cbox);
01315 }
01316 
01317 static void
01318 create_playlist_category(void)
01319 {
01320     GtkWidget *vbox5;
01321     GtkWidget *alignment55;
01322     GtkWidget *label60;
01323     GtkWidget *alignment56;
01324     GtkWidget *table6;
01325     GtkWidget *titlestring_help_button;
01326     GtkWidget *image1;
01327     GtkWidget *label62;
01328     GtkWidget *label61;
01329     GtkWidget *alignment85;
01330     GtkWidget *label84;
01331     GtkWidget *alignment86;
01332     GtkWidget *hbox9;
01333     GtkWidget *vbox34;
01334     GtkWidget *image8;
01335     GtkWidget *titlestring_tag_menu = create_titlestring_tag_menu();
01336     GtkWidget * numbers_alignment, * numbers;
01337 
01338     vbox5 = gtk_vbox_new (FALSE, 0);
01339     gtk_container_add ((GtkContainer *) category_notebook, vbox5);
01340 
01341     create_widgets(GTK_BOX(vbox5), playlist_page_widgets, G_N_ELEMENTS(playlist_page_widgets));
01342 
01343     alignment55 = gtk_alignment_new (0.5, 0.5, 1, 1);
01344     gtk_box_pack_start (GTK_BOX (vbox5), alignment55, FALSE, FALSE, 0);
01345     gtk_alignment_set_padding ((GtkAlignment *) alignment55, 12, 3, 0, 0);
01346 
01347     label60 = gtk_label_new (_("<b>Song Display</b>"));
01348     gtk_container_add (GTK_CONTAINER (alignment55), label60);
01349     gtk_label_set_use_markup (GTK_LABEL (label60), TRUE);
01350     gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5);
01351 
01352     numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
01353     gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
01354     gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
01355 
01356     numbers = gtk_check_button_new_with_label (_("Show song numbers"));
01357     gtk_toggle_button_set_active ((GtkToggleButton *) numbers,
01358      get_bool (NULL, "show_numbers_in_pl"));
01359     g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
01360      show_numbers_cb, 0);
01361     gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
01362 
01363     numbers_alignment = gtk_alignment_new (0, 0, 0, 0);
01364     gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0);
01365     gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3);
01366 
01367     numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 "
01368      "instead of 2:00)"));
01369     gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero"));
01370     g_signal_connect ((GObject *) numbers, "toggled", (GCallback)
01371      leading_zero_cb, 0);
01372     gtk_container_add ((GtkContainer *) numbers_alignment, numbers);
01373 
01374     alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1);
01375     gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0);
01376     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0);
01377 
01378     table6 = gtk_table_new (2, 3, FALSE);
01379     gtk_container_add (GTK_CONTAINER (alignment56), table6);
01380     gtk_table_set_row_spacings (GTK_TABLE (table6), 4);
01381     gtk_table_set_col_spacings (GTK_TABLE (table6), 12);
01382 
01383     titlestring_help_button = gtk_button_new ();
01384     gtk_table_attach (GTK_TABLE (table6), titlestring_help_button, 2, 3, 1, 2,
01385                       (GtkAttachOptions) (0),
01386                       (GtkAttachOptions) (0), 0, 0);
01387 
01388     gtk_widget_set_can_focus (titlestring_help_button, FALSE);
01389     gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format"));
01390     gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF);
01391     gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE);
01392 
01393     image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON);
01394     gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1);
01395 
01396     GtkWidget * titlestring_cbox;
01397     create_titlestring_widgets (& titlestring_cbox, & titlestring_entry);
01398     gtk_table_attach (GTK_TABLE (table6), titlestring_cbox, 1, 3, 0, 1,
01399                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
01400                       (GtkAttachOptions) (0), 0, 0);
01401     gtk_table_attach (GTK_TABLE (table6), titlestring_entry, 1, 2, 1, 2,
01402                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
01403                       (GtkAttachOptions) (0), 0, 0);
01404 
01405     label62 = gtk_label_new (_("Custom string:"));
01406     gtk_table_attach (GTK_TABLE (table6), label62, 0, 1, 1, 2,
01407                       (GtkAttachOptions) (0),
01408                       (GtkAttachOptions) (0), 0, 0);
01409     gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT);
01410     gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5);
01411 
01412     label61 = gtk_label_new (_("Title format:"));
01413     gtk_table_attach (GTK_TABLE (table6), label61, 0, 1, 0, 1,
01414                       (GtkAttachOptions) (0),
01415                       (GtkAttachOptions) (0), 0, 0);
01416     gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT);
01417     gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5);
01418 
01419     alignment85 = gtk_alignment_new (0.5, 0.5, 1, 1);
01420     gtk_box_pack_start (GTK_BOX (vbox5), alignment85, FALSE, FALSE, 0);
01421     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment85), 12, 12, 0, 0);
01422 
01423     label84 = gtk_label_new (_("<b>Popup Information</b>"));
01424     gtk_container_add (GTK_CONTAINER (alignment85), label84);
01425     gtk_label_set_use_markup (GTK_LABEL (label84), TRUE);
01426     gtk_misc_set_alignment (GTK_MISC (label84), 0, 0.5);
01427 
01428     alignment86 = gtk_alignment_new (0.5, 0.5, 1, 1);
01429     gtk_box_pack_start (GTK_BOX (vbox5), alignment86, FALSE, FALSE, 0);
01430     gtk_alignment_set_padding (GTK_ALIGNMENT (alignment86), 0, 0, 12, 0);
01431 
01432     hbox9 = gtk_hbox_new (FALSE, 12);
01433     gtk_container_add (GTK_CONTAINER (alignment86), hbox9);
01434 
01435     vbox34 = gtk_vbox_new (FALSE, 0);
01436     gtk_box_pack_start (GTK_BOX (hbox9), vbox34, TRUE, TRUE, 0);
01437 
01438     filepopupbutton = gtk_check_button_new_with_mnemonic (_("Show popup information for playlist entries"));
01439     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."));
01440     gtk_toggle_button_set_active ((GtkToggleButton *) filepopupbutton,
01441      get_bool (NULL, "show_filepopup_for_tuple"));
01442     gtk_box_pack_start ((GtkBox *) vbox34, filepopupbutton, TRUE, FALSE, 0);
01443 
01444     filepopup_settings_button = gtk_button_new ();
01445     gtk_widget_set_sensitive (filepopup_settings_button,
01446      get_bool (NULL, "show_filepopup_for_tuple"));
01447     gtk_box_pack_start (GTK_BOX (hbox9), filepopup_settings_button, FALSE, FALSE, 0);
01448 
01449     gtk_widget_set_can_focus (filepopup_settings_button, FALSE);
01450     gtk_widget_set_tooltip_text (filepopup_settings_button, _("Edit settings for popup information"));
01451     gtk_button_set_relief (GTK_BUTTON (filepopup_settings_button), GTK_RELIEF_HALF);
01452 
01453     image8 = gtk_image_new_from_stock ("gtk-properties", GTK_ICON_SIZE_BUTTON);
01454     gtk_container_add (GTK_CONTAINER (filepopup_settings_button), image8);
01455 
01456 
01457 
01458     g_signal_connect (filepopupbutton, "toggled",
01459                      G_CALLBACK(on_show_filepopup_toggled),
01460                      NULL);
01461     g_signal_connect(G_OBJECT(filepopup_settings_button), "clicked",
01462                      G_CALLBACK(on_filepopup_settings_clicked),
01463                      NULL);
01464 
01465     g_signal_connect(titlestring_help_button, "clicked",
01466                      G_CALLBACK(on_titlestring_help_button_clicked),
01467                      titlestring_tag_menu);
01468 
01469     /* Create window for filepopup settings */
01470     create_filepopup_settings();
01471 }
01472 
01473 static GtkWidget * output_config_button, * output_about_button;
01474 
01475 static bool_t output_enum_cb (PluginHandle * plugin, GList * * list)
01476 {
01477     * list = g_list_prepend (* list, plugin);
01478     return TRUE;
01479 }
01480 
01481 static GList * output_get_list (void)
01482 {
01483     static GList * list = NULL;
01484 
01485     if (list == NULL)
01486     {
01487         plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_enum_cb,
01488          & list);
01489         list = g_list_reverse (list);
01490     }
01491 
01492     return list;
01493 }
01494 
01495 static void output_combo_update (GtkComboBox * combo)
01496 {
01497     PluginHandle * plugin = plugin_get_current (PLUGIN_TYPE_OUTPUT);
01498     gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin));
01499     gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin));
01500     gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin));
01501 }
01502 
01503 static void output_combo_changed (GtkComboBox * combo)
01504 {
01505     PluginHandle * plugin = g_list_nth_data (output_get_list (),
01506      gtk_combo_box_get_active (combo));
01507     g_return_if_fail (plugin != NULL);
01508 
01509     plugin_enable (plugin, TRUE);
01510     output_combo_update (combo);
01511 }
01512 
01513 static void output_combo_fill (GtkComboBox * combo)
01514 {
01515     for (GList * node = output_get_list (); node != NULL; node = node->next)
01516         gtk_combo_box_text_append_text ((GtkComboBoxText *) combo,
01517          plugin_get_name (node->data));
01518 }
01519 
01520 static void output_do_config (void)
01521 {
01522     OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
01523     g_return_if_fail (op != NULL);
01524     if (op->configure != NULL)
01525         op->configure ();
01526     else if (op->settings != NULL)
01527         plugin_preferences_show (op->settings);
01528 }
01529 
01530 static void output_do_about (void)
01531 {
01532     OutputPlugin * op = plugin_get_header (output_plugin_get_current ());
01533     g_return_if_fail (op != NULL);
01534     if (op->about != NULL)
01535         op->about ();
01536 }
01537 
01538 static void * create_output_plugin_box (void)
01539 {
01540     GtkWidget * hbox1 = gtk_hbox_new (FALSE, 6);
01541     gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0);
01542 
01543     GtkWidget * vbox = gtk_vbox_new (FALSE, 6);
01544     gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0);
01545 
01546     GtkWidget * hbox2 = gtk_hbox_new (FALSE, 6);
01547     gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0);
01548 
01549     GtkWidget * output_plugin_cbox = gtk_combo_box_text_new ();
01550     gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0);
01551 
01552     GtkWidget * hbox3 = gtk_hbox_new (FALSE, 6);
01553     gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0);
01554 
01555     output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES);
01556     gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0);
01557 
01558     output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT);
01559     gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0);
01560 
01561     output_combo_fill ((GtkComboBox *) output_plugin_cbox);
01562     output_combo_update ((GtkComboBox *) output_plugin_cbox);
01563 
01564     g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL);
01565     g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL);
01566     g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL);
01567 
01568     return hbox1;
01569 }
01570 
01571 static void create_audio_category (void)
01572 {
01573     GtkWidget * audio_page_vbox = gtk_vbox_new (FALSE, 0);
01574     create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets));
01575     gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox);
01576 }
01577 
01578 static void
01579 create_connectivity_category(void)
01580 {
01581     GtkWidget *connectivity_page_vbox;
01582     GtkWidget *vbox29;
01583 
01584     connectivity_page_vbox = gtk_vbox_new (FALSE, 0);
01585     gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox);
01586 
01587     vbox29 = gtk_vbox_new (FALSE, 0);
01588     gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0);
01589 
01590     create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets));
01591 }
01592 
01593 static void create_plugin_category (void)
01594 {
01595     GtkWidget * notebook = gtk_notebook_new ();
01596     gtk_container_add ((GtkContainer *) category_notebook, notebook);
01597 
01598     int types[] = {PLUGIN_TYPE_TRANSPORT, PLUGIN_TYPE_PLAYLIST,
01599      PLUGIN_TYPE_INPUT, PLUGIN_TYPE_EFFECT, PLUGIN_TYPE_VIS, PLUGIN_TYPE_GENERAL};
01600     const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"),
01601      N_("Effect"), N_("Visualization"), N_("General")};
01602 
01603     for (int i = 0; i < G_N_ELEMENTS (types); i ++)
01604         gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new
01605          (types[i]), gtk_label_new (_(names[i])));
01606 }
01607 
01608 static bool_t
01609 prefswin_destroy(GtkWidget *window, GdkEvent *event, gpointer data)
01610 {
01611     prefswin = NULL;
01612     category_notebook = NULL;
01613     gtk_widget_destroy(filepopup_settings);
01614     filepopup_settings = NULL;
01615     gtk_widget_destroy(window);
01616     return TRUE;
01617 }
01618 
01619 /* GtkWidget * * create_prefs_window (void) */
01620 void * * create_prefs_window (void)
01621 {
01622     char *aud_version_string;
01623 
01624     GtkWidget *vbox;
01625     GtkWidget *hbox1;
01626     GtkWidget *scrolledwindow6;
01627     GtkWidget *hseparator1;
01628     GtkWidget *hbox4;
01629     GtkWidget *audversionlabel;
01630     GtkWidget *prefswin_button_box;
01631     GtkWidget *hbox11;
01632     GtkWidget *image10;
01633     GtkWidget *close;
01634     GtkAccelGroup *accel_group;
01635 
01636     accel_group = gtk_accel_group_new ();
01637 
01638     prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL);
01639     gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG);
01640     gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12);
01641     gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences"));
01642     gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER);
01643     gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400);
01644 
01645     vbox = gtk_vbox_new (FALSE, 0);
01646     gtk_container_add (GTK_CONTAINER (prefswin), vbox);
01647 
01648     hbox1 = gtk_hbox_new (FALSE, 8);
01649     gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0);
01650 
01651     scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL);
01652     gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0);
01653     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
01654     gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN);
01655 
01656     category_treeview = gtk_tree_view_new ();
01657     gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview);
01658     gtk_widget_set_size_request (scrolledwindow6, 168, -1);
01659     gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE);
01660 
01661     category_notebook = gtk_notebook_new ();
01662     gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0);
01663 
01664     gtk_widget_set_can_focus (category_notebook, FALSE);
01665     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE);
01666     gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE);
01667     gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE);
01668 
01669     create_audio_category();
01670     create_connectivity_category();
01671     create_playlist_category();
01672     create_plugin_category();
01673 
01674     hseparator1 = gtk_hseparator_new ();
01675     gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6);
01676 
01677     hbox4 = gtk_hbox_new (FALSE, 0);
01678     gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0);
01679 
01680     audversionlabel = gtk_label_new ("");
01681     gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0);
01682     gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE);
01683 
01684     prefswin_button_box = gtk_hbutton_box_new ();
01685     gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0);
01686     gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END);
01687     gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6);
01688 
01689     hbox11 = gtk_hbox_new (FALSE, 2);
01690 
01691     image10 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
01692     gtk_box_pack_start (GTK_BOX (hbox11), image10, FALSE, FALSE, 0);
01693 
01694     close = gtk_button_new_from_stock ("gtk-close");
01695     gtk_container_add (GTK_CONTAINER (prefswin_button_box), close);
01696     gtk_widget_set_can_default(close, TRUE);
01697     gtk_widget_add_accelerator (close, "clicked", accel_group,
01698                                 GDK_Escape, (GdkModifierType) 0,
01699                                 GTK_ACCEL_VISIBLE);
01700 
01701 
01702     gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group);
01703 
01704     /* connect signals */
01705     g_signal_connect(G_OBJECT(prefswin), "delete_event",
01706                      G_CALLBACK(prefswin_destroy),
01707                      NULL);
01708     g_signal_connect_swapped(G_OBJECT(close), "clicked",
01709                              G_CALLBACK(prefswin_destroy),
01710                              prefswin);
01711 
01712     /* create category view */
01713     fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook);
01714 
01715     /* audacious version label */
01716 
01717     aud_version_string = g_strdup_printf
01718      ("<span size='small'>%s (%s)</span>", "Audacious " VERSION, BUILDSTAMP);
01719 
01720     gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string );
01721     g_free(aud_version_string);
01722     gtk_widget_show_all(vbox);
01723 
01724     return & prefswin;
01725 }
01726 
01727 void
01728 destroy_prefs_window(void)
01729 {
01730     prefswin_destroy(prefswin, NULL, NULL);
01731 }
01732 
01733 void show_prefs_window (void)
01734 {
01735     if (! prefswin)
01736         create_prefs_window ();
01737 
01738     gtk_window_present ((GtkWindow *) prefswin);
01739 }
01740 
01741 void
01742 hide_prefs_window(void)
01743 {
01744     g_return_if_fail(prefswin);
01745     gtk_widget_hide(GTK_WIDGET(prefswin));
01746 }
01747 
01748 static void prefswin_page_queue_new (GtkWidget * container, const char * name,
01749  const char * imgurl)
01750 {
01751     CategoryQueueEntry *ent = g_new0(CategoryQueueEntry, 1);
01752 
01753     ent->container = container;
01754     ent->pg_name = name;
01755     ent->img_url = imgurl;
01756 
01757     if (category_queue)
01758         ent->next = category_queue;
01759 
01760     category_queue = ent;
01761 }
01762 
01763 static void
01764 prefswin_page_queue_destroy(CategoryQueueEntry *ent)
01765 {
01766     category_queue = ent->next;
01767     g_free(ent);
01768 }
01769 
01770 /*
01771  * Public APIs for adding new pages to the prefs window.
01772  *
01773  * Basically, the concept here is that third party components can register themselves in the root
01774  * preferences window.
01775  *
01776  * From a usability standpoint this makes the application look more "united", instead of cluttered
01777  * and malorganised. Hopefully this option will be used further in the future.
01778  *
01779  *    - nenolod
01780  */
01781 /* int prefswin_page_new (GtkWidget * container, const char * name,
01782  const char * imgurl) */
01783 int prefswin_page_new (void * container, const char * name, const char *
01784  imgurl)
01785 {
01786     GtkTreeModel *model;
01787     GtkTreeIter iter;
01788     GdkPixbuf *img = NULL;
01789     GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
01790     int id;
01791 
01792     if (treeview == NULL || category_notebook == NULL)
01793     {
01794         prefswin_page_queue_new(container, name, imgurl);
01795         return -1;
01796     }
01797 
01798     model = gtk_tree_view_get_model(treeview);
01799 
01800     if (model == NULL)
01801     {
01802         prefswin_page_queue_new(container, name, imgurl);
01803         return -1;
01804     }
01805 
01806     /* Make sure the widgets are visible. */
01807     gtk_widget_show(container);
01808     id = gtk_notebook_append_page(GTK_NOTEBOOK(category_notebook), container, NULL);
01809 
01810     if (id == -1)
01811         return -1;
01812 
01813     if (imgurl != NULL)
01814         img = gdk_pixbuf_new_from_file(imgurl, NULL);
01815 
01816     gtk_list_store_append(GTK_LIST_STORE(model), &iter);
01817     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
01818                        CATEGORY_VIEW_COL_ICON, img,
01819                        CATEGORY_VIEW_COL_NAME,
01820                        name, CATEGORY_VIEW_COL_ID, id, -1);
01821 
01822     if (img != NULL)
01823         g_object_unref(img);
01824 
01825     return id;
01826 }
01827 
01828 void
01829 prefswin_page_destroy(GtkWidget *container)
01830 {
01831     GtkTreeModel *model;
01832     GtkTreeIter iter;
01833     GtkTreeView *treeview = GTK_TREE_VIEW(category_treeview);
01834     bool_t ret;
01835     int id;
01836     int index = -1;
01837 
01838     if (category_notebook == NULL || treeview == NULL || container == NULL)
01839         return;
01840 
01841     id = gtk_notebook_page_num(GTK_NOTEBOOK(category_notebook), container);
01842 
01843     if (id == -1)
01844         return;
01845 
01846     gtk_notebook_remove_page(GTK_NOTEBOOK(category_notebook), id);
01847 
01848     model = gtk_tree_view_get_model(treeview);
01849 
01850     if (model == NULL)
01851         return;
01852 
01853     ret = gtk_tree_model_get_iter_first(model, &iter);
01854 
01855     while (ret == TRUE)
01856     {
01857         gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1);
01858 
01859         if (index == id)
01860         {
01861             gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
01862             ret = gtk_tree_model_get_iter_first(model, &iter);
01863             continue;
01864         }
01865 
01866         if (index > id)
01867         {
01868             index--;
01869             gtk_list_store_set(GTK_LIST_STORE(model), &iter, CATEGORY_VIEW_COL_ID, index, -1);
01870         }
01871 
01872         ret = gtk_tree_model_iter_next(model, &iter);
01873     }
01874 }
01875 
01876 static void sw_volume_toggled (void)
01877 {
01878     int vol[2];
01879 
01880     if (get_bool (NULL, "software_volume_control"))
01881     {
01882         vol[0] = get_int (NULL, "sw_volume_left");
01883         vol[1] = get_int (NULL, "sw_volume_right");
01884     }
01885     else
01886         playback_get_volume (& vol[0], & vol[1]);
01887 
01888     hook_call ("volume set", vol);
01889 }