X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/3849817f8cbfc366a5e9979c190b2f1d604f4021..ec7109f36e878732d30e592279c2b160b28e6955:/disobedience/control.c diff --git a/disobedience/control.c b/disobedience/control.c index 92532c4..6ed9fb3 100644 --- a/disobedience/control.c +++ b/disobedience/control.c @@ -26,17 +26,8 @@ /* Forward declarations ---------------------------------------------------- */ -WT(adjustment); -WT(hscale); -WT(hbox); -WT(button); -WT(image); -WT(label); -WT(vbox); - struct icon; -static void update_icon(const struct icon *icon); static void clicked_icon(GtkButton *, gpointer); static void clicked_menu(GtkMenuItem *, gpointer userdata); static void toggled_menu(GtkCheckMenuItem *, gpointer userdata); @@ -57,6 +48,9 @@ static void volume_adjusted(GtkAdjustment *a, gpointer user_data); static gchar *format_volume(GtkScale *scale, gdouble value); static gchar *format_balance(GtkScale *scale, gdouble value); +static void icon_changed(const char *event, + void *evendata, + void *callbackdata); static void volume_changed(const char *event, void *eventdata, void *callbackdata); @@ -93,6 +87,9 @@ struct icon { /** @brief Associated menu item or NULL */ const char *menuitem; + /** @brief Events that change this icon, separated by spaces */ + const char *events; + /** @brief @ref eclient.h function to call to go from off to on * * For action buttons, this should be NULL. @@ -131,24 +128,32 @@ struct icon { GtkWidget *image_off; }; -/* TODO: Add rights into the mix below */ - static int pause_resume_on(void) { return !(last_state & DISORDER_TRACK_PAUSED); } static int pause_resume_sensitive(void) { - return !!(last_state & DISORDER_PLAYING); + return !!(last_state & DISORDER_PLAYING) + && (last_rights & RIGHT_PAUSE); } static int scratch_sensitive(void) { - return !!(last_state & DISORDER_PLAYING); + return !!(last_state & DISORDER_PLAYING) + && right_scratchable(last_rights, config->username, playing_track); +} + +static int random_sensitive(void) { + return !!(last_rights & RIGHT_GLOBAL_PREFS); } static int random_enabled(void) { return !!(last_state & DISORDER_RANDOM_ENABLED); } +static int playing_sensitive(void) { + return !!(last_rights & RIGHT_GLOBAL_PREFS); +} + static int playing_enabled(void) { return !!(last_state & DISORDER_PLAYING_ENABLED); } @@ -173,6 +178,7 @@ static struct icon icons[] = { sensitive: pause_resume_sensitive, action_go_on: disorder_eclient_resume, action_go_off: disorder_eclient_pause, + events: "pause-changed playing-changed rights-changed", }, { icon_on: "cross.png", @@ -180,6 +186,7 @@ static struct icon icons[] = { menuitem: "/Control/Scratch", sensitive: scratch_sensitive, action_go_off: disorder_eclient_scratch_playing, + events: "playing-track-changed rights-changed", }, { icon_on: "randomcross.png", @@ -188,8 +195,10 @@ static struct icon icons[] = { tip_off: "Enable random play", menuitem: "/Control/Random play", on: random_enabled, + sensitive: random_sensitive, action_go_on: disorder_eclient_random_enable, action_go_off: disorder_eclient_random_disable, + events: "random-changed rights-changed", }, { icon_on: "notescross.png", @@ -197,8 +206,10 @@ static struct icon icons[] = { icon_off: "notes.png", tip_off: "Enable play", on: playing_enabled, + sensitive: playing_sensitive, action_go_on: disorder_eclient_enable, action_go_off: disorder_eclient_disable, + events: "enabled-changed rights-changed", }, { icon_on: "speakercross.png", @@ -209,7 +220,8 @@ static struct icon icons[] = { on: rtp_enabled, sensitive: rtp_sensitive, action_go_on: enable_rtp, - action_go_off: disable_rtp + action_go_off: disable_rtp, + events: "rtp-changed", }, }; @@ -221,41 +233,15 @@ static GtkAdjustment *balance_adj; static GtkWidget *volume_widget; static GtkWidget *balance_widget; -/** @brief Called whenever last_state changes in any way - * - * TODO we should only update things that have changed. - */ -static void control_changed(const char attribute((unused)) *event, - void attribute((unused)) *evendata, - void attribute((unused)) *callbackdata) { - int n; - gboolean volume_supported; - - D(("control_changed")); - /* Update all the icons */ - for(n = 0; n < NICONS; ++n) - update_icon(&icons[n]); - /* Only display volume/balance controls if they will work */ - if(!rtp_supported - || (rtp_supported && mixer_supported(DEFAULT_BACKEND))) - volume_supported = TRUE; - else - volume_supported = FALSE; - (volume_supported ? gtk_widget_show : gtk_widget_hide)(volume_widget); - (volume_supported ? gtk_widget_show : gtk_widget_hide)(balance_widget); -} - /** @brief Create the control bar */ GtkWidget *control_widget(void) { GtkWidget *hbox = gtk_hbox_new(FALSE, 1), *vbox; int n; - NW(hbox); D(("control_widget")); assert(mainmenufactory); /* ordering must be right */ for(n = 0; n < NICONS; ++n) { /* Create the button */ - NW(button); icons[n].button = gtk_button_new(); gtk_widget_set_style(icons[n].button, tool_style); icons[n].image_on = gtk_image_new_from_pixbuf(find_image(icons[n].icon_on)); @@ -271,7 +257,6 @@ GtkWidget *control_widget(void) { G_CALLBACK(clicked_icon), &icons[n]); /* pop the icon in a vbox so it doesn't get vertically stretch if there are * taller things in the control bar */ - NW(vbox); vbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), icons[n].button, TRUE, FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0); @@ -286,19 +271,20 @@ GtkWidget *control_widget(void) { g_signal_connect(G_OBJECT(icons[n].item), "activate", G_CALLBACK(clicked_menu), &icons[n]); } + /* Make sure the icon is updated when relevant things changed */ + char **events = split(icons[n].events, 0, 0, 0, 0); + while(*events) + event_register(*events++, icon_changed, &icons[n]); + event_register("connected-changed", icon_changed, &icons[n]); } /* create the adjustments for the volume control */ - NW(adjustment); volume_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, goesupto, goesupto / 20, goesupto / 20, 0)); - NW(adjustment); balance_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1, 1, 0.2, 0.2, 0)); /* the volume control */ - NW(hscale); volume_widget = gtk_hscale_new(volume_adj); - NW(hscale); balance_widget = gtk_hscale_new(balance_adj); gtk_widget_set_style(volume_widget, tool_style); gtk_widget_set_style(balance_widget, tool_style); @@ -323,12 +309,8 @@ GtkWidget *control_widget(void) { G_CALLBACK(format_volume), 0); g_signal_connect(G_OBJECT(balance_widget), "format-value", G_CALLBACK(format_balance), 0); - event_register("enabled-changed", control_changed, 0); - event_register("random-changed", control_changed, 0); - event_register("pause-changed", control_changed, 0); - event_register("playing-changed", control_changed, 0); - event_register("rtp-changed", control_changed, 0); event_register("volume-changed", volume_changed, 0); + event_register("rtp-changed", volume_changed, 0); return hbox; } @@ -337,28 +319,49 @@ static void volume_changed(const char attribute((unused)) *event, void attribute((unused)) *eventdata, void attribute((unused)) *callbackdata) { double l, r; + gboolean volume_supported; D(("volume_changed")); - l = volume_l / 100.0; - r = volume_r / 100.0; ++suppress_actions; - gtk_adjustment_set_value(volume_adj, volume(l, r) * goesupto); - gtk_adjustment_set_value(balance_adj, balance(l, r)); + /* Only display volume/balance controls if they will work */ + if(!rtp_supported + || (rtp_supported && mixer_supported(DEFAULT_BACKEND))) + volume_supported = TRUE; + else + volume_supported = FALSE; + /* TODO: if the server doesn't know how to set the volume [but isn't using + * network play] then we should have volume_supported = FALSE */ + if(volume_supported) { + gtk_widget_show(volume_widget); + gtk_widget_show(balance_widget); + l = volume_l / 100.0; + r = volume_r / 100.0; + gtk_adjustment_set_value(volume_adj, volume(l, r) * goesupto); + gtk_adjustment_set_value(balance_adj, balance(l, r)); + } else { + gtk_widget_hide(volume_widget); + gtk_widget_hide(balance_widget); + } --suppress_actions; } /** @brief Update the state of one of the control icons - * @param icon Target icon */ -static void update_icon(const struct icon *icon) { +static void icon_changed(const char attribute((unused)) *event, + void attribute((unused)) *evendata, + void *callbackdata) { + //fprintf(stderr, "icon_changed (%s)\n", event); + const struct icon *const icon = callbackdata; int on = icon->on ? icon->on() : 1; int sensitive = icon->sensitive ? icon->sensitive() : 1; + //fprintf(stderr, "sensitive->%d\n", sensitive); GtkWidget *child, *newchild; ++suppress_actions; /* If the connection is down nothing is ever usable */ if(!(last_state & DISORDER_CONNECTED)) sensitive = 0; + //fprintf(stderr, "(checked connected) sensitive->%d\n", sensitive); /* Replace the child */ newchild = on ? icon->image_on : icon->image_off; child = gtk_bin_get_child(GTK_BIN(icon->button)); @@ -368,6 +371,11 @@ static void update_icon(const struct icon *icon) { gtk_container_add(GTK_CONTAINER(icon->button), newchild); gtk_widget_show(newchild); } + /* If you disable play or random play NOT via the icon (for instance, via the + * edit menu or via a completely separate command line invocation) then the + * icon shows up as insensitive. Hover the mouse over it and the correct + * state is immediately displayed. sensitive and GTK_WIDGET_SENSITIVE show + * it to be in the correct state, so I think this is may be a GTK+ bug. */ if(icon->tip_on) gtk_tooltips_set_tip(tips, icon->button, on ? icon->tip_on : icon->tip_off, ""); @@ -382,9 +390,9 @@ static void update_icon(const struct icon *icon) { } static void icon_action_completed(void attribute((unused)) *v, - const char *error) { - if(error) - popup_protocol_error(0, error); + const char *err) { + if(err) + popup_protocol_error(0, err); } static void clicked_icon(GtkButton attribute((unused)) *button, @@ -411,11 +419,11 @@ static void toggled_menu(GtkCheckMenuItem attribute((unused)) *menuitem, /** @brief Called when a volume command completes */ static void volume_completed(void attribute((unused)) *v, - const char *error, + const char *err, int attribute((unused)) l, int attribute((unused)) r) { - if(error) - popup_protocol_error(0, error); + if(err) + popup_protocol_error(0, err); /* We don't set the UI's notion of the volume here, it is set from the log * regardless of the reason it changed */ }