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