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