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