From fad38688c14d57684af46d1c135c41a9687f501a Mon Sep 17 00:00:00 2001 From: Bert Burgemeister Date: Sat, 28 May 2016 23:45:50 +0200 Subject: [PATCH] Support temporary blocking of feedback messages --- .gitignore | 1 + Makefile | 2 +- NEWS | 1 + pipeglade.1 | 13 ++++ pipeglade.c | 207 ++++++++++++++++++++++++++++++++++++------------------- pipeglade.ui | 6 +- pipegladetest.sh | 31 +++++++-- 7 files changed, 180 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index 12718f5..a37bd9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.core *.eps *.epsf +*.err *.gmon *.gprof *.log diff --git a/Makefile b/Makefile index 54afe73..e696f5a 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,7 @@ gh-pages/index.html gh-pages/pipeglade.1.html: pipeglade.1 www-template/index.ht cp www-template/* gh-pages/ cp clock.sh gh-pages/clock.sh.txt cp clock.ui gh-pages/clock.ui.txt - mandoc -Wall -T html -O style=style.css pipeglade.1 > gh-pages/pipeglade.1.html + mandoc -Wall -T xhtml -O style=style.css pipeglade.1 > gh-pages/pipeglade.1.html mandoc -Wall -T pdf -O paper=a4 pipeglade.1 > gh-pages/pipeglade.1.pdf cp LICENSE gh-pages/ echo -e '/@/\ns//\>/\n,s/^$$/

/\nwq' | ed -s gh-pages/LICENSE diff --git a/NEWS b/NEWS index 7b46dde..29be1ba 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ command execution. * Add command line option -O for redirecting stderr. * Reject commands with excess arguments. + * Support temporary blocking of feedback messages. * GtkDialog and GtkFileChooserDialog now respond to the commands that work for GtkWindow. * Toplevel windows report 'name:closed' when closed by the window diff --git a/pipeglade.1 b/pipeglade.1 index 0196885..46e4202 100644 --- a/pipeglade.1 +++ b/pipeglade.1 @@ -300,6 +300,19 @@ sets the widget's tooltip .Ar text . Default is disabling the tooltip. .Pp +.Qq Ar id Ns Cm :block Brq Cm 0 | 1 +blocks +.Pq Cm 1 +or unblocks +.Pq Cm 0 +feedback messages from widget. +Doesn't work for +.Sx GtkTreeView ; +address the underlying +.Cm GtkTreeSelection +instead. +Initially, all widgets are unblocked. +.Pp .Qq Ar id Ns Cm :force simulates a click on widget .Ar id , diff --git a/pipeglade.c b/pipeglade.c index 7f53950..55692f4 100644 --- a/pipeglade.c +++ b/pipeglade.c @@ -1995,6 +1995,34 @@ update_label(struct ui_data *ud) ign_cmd(ud->type, ud->msg); } +struct handler_id { + unsigned int id; /* returned by g_signal_connect() and friends */ + bool blocked; /* we avoid multiple blocking/unblocking */ + struct handler_id *next; +}; + +static void +update_blocked(struct ui_data *ud) +{ + char dummy; + struct handler_id *hid; + unsigned long int val; + + if (sscanf(ud->data, "%lu %c", &val, &dummy) == 1 && val < 2) { + for (hid = g_object_get_data(ud->obj, "signal-id"); + hid != NULL; hid = hid->next) { + if (val == 0 && hid->blocked == true) { + g_signal_handler_unblock(ud->obj, hid->id); + hid->blocked = false; + } else if (val == 1 && hid->blocked == false) { + g_signal_handler_block(ud->obj, hid->id); + hid->blocked = true; + } + } + } else + ign_cmd(ud->type, ud->msg); +} + static void update_notebook(struct ui_data *ud) { @@ -2695,7 +2723,6 @@ update_ui(struct ui_data *ud) char *lc = lc_numeric(); (ud->fn)(ud); - /* (ud->fn)(ud->obj, ud->action, ud->data, ud->msg, ud->type, ud->args); */ free(ud->msg_tokens); free(ud->msg); free(ud); @@ -2804,6 +2831,8 @@ digest_msg(struct info *ar) ud->type = G_TYPE_FROM_INSTANCE(ud->obj); if (eql(ud->action, "force")) ud->fn = fake_ui_activity; + else if (eql(ud->action, "block")) + ud->fn = update_blocked; else if (eql(ud->action, "set_sensitive")) ud->fn = update_sensitivity; else if (eql(ud->action, "set_visible")) @@ -3006,6 +3035,44 @@ cb_tree_model_toggle(GtkCellRenderer *renderer, gchar *path_s, struct info *ar) toggle_state? "0" : "1"); } +/* + * Add new element containing id to the list of callback-handler ids + * stored in obj's field named "signal_id" + */ +static void +push_handler_id(gpointer *obj, unsigned int id) +{ + struct handler_id *prev_hid, *hid; + + prev_hid = g_object_get_data(G_OBJECT(obj), "signal-id"); + if ((hid = malloc(sizeof(struct handler_id))) == NULL) + OOM_ABORT; + hid->next = prev_hid; + hid->id = id; + hid->blocked = false; + g_object_set_data(G_OBJECT(obj), "signal-id", hid); +} + +/* + * Connect function cb to obj's widget signal sig, remembering the + * handler id in a list in obj's field named "signal-id" + */ +static void +sig_conn(gpointer *obj, char *sig, GCallback cb, struct info *ar) +{ + unsigned int handler_id = g_signal_connect(obj, sig, cb, ar); + + push_handler_id(obj, handler_id); +} + +static void +sig_conn_swapped(gpointer *obj, char *sig, GCallback cb, void *data) +{ + unsigned int handler_id = g_signal_connect_swapped(obj, sig, cb, data); + + push_handler_id(obj, handler_id); +} + static void connect_widget_signals(gpointer *obj, struct info *ar) { @@ -3026,7 +3093,7 @@ connect_widget_signals(gpointer *obj, struct info *ar) unsigned int i, n_cells = g_list_length(cells); g_list_free(cells); - g_signal_connect(obj, "clicked", G_CALLBACK(cb_simple), info_txt_new(o, "clicked")); + sig_conn(obj, "clicked", G_CALLBACK(cb_simple), info_txt_new(o, "clicked")); for (i = 1; i <= n_cells; i++) { GtkCellRenderer *renderer; gboolean editable = FALSE; @@ -3058,14 +3125,14 @@ connect_widget_signals(gpointer *obj, struct info *ar) /* Button associated with a GtkTextView. */ if ((suffix = strstr(w_id, "_send_text")) != NULL && GTK_IS_TEXT_VIEW(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) - g_signal_connect(obj, "clicked", G_CALLBACK(cb_send_text), - info_obj_new(o, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2))), NULL)); + sig_conn(obj, "clicked", G_CALLBACK(cb_send_text), + info_obj_new(o, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2))), NULL)); else if ((suffix = strstr(w_id, "_send_selection")) != NULL && GTK_IS_TEXT_VIEW(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) - g_signal_connect(obj, "clicked", G_CALLBACK(cb_send_text_selection), - info_obj_new(o, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2))), NULL)); + sig_conn(obj, "clicked", G_CALLBACK(cb_send_text_selection), + info_obj_new(o, G_OBJECT(gtk_text_view_get_buffer(GTK_TEXT_VIEW(obj2))), NULL)); else { - g_signal_connect(obj, "clicked", G_CALLBACK(cb_simple), info_txt_new(o, "clicked")); + sig_conn(obj, "clicked", G_CALLBACK(cb_simple), info_txt_new(o, "clicked")); /* Buttons associated with (and part of) a GtkDialog. * (We shun response ids which could be returned from * gtk_dialog_run() because that would require the @@ -3074,102 +3141,102 @@ connect_widget_signals(gpointer *obj, struct info *ar) if ((suffix = strstr(w_id, "_cancel")) != NULL && GTK_IS_DIALOG(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) if (eql(widget_id(GTK_BUILDABLE(obj2)), MAIN_WIN)) - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(gtk_main_quit), NULL); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(gtk_main_quit), NULL); else - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(gtk_widget_hide), obj2); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(gtk_widget_hide), obj2); else if ((suffix = strstr(w_id, "_ok")) != NULL && GTK_IS_DIALOG(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) { if (GTK_IS_FILE_CHOOSER_DIALOG(obj2)) - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(cb_send_file_chooser_dialog_selection), - info_obj_new(o, obj2, NULL)); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(cb_send_file_chooser_dialog_selection), + info_obj_new(o, obj2, NULL)); if (eql(widget_id(GTK_BUILDABLE(obj2)), MAIN_WIN)) - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(gtk_main_quit), NULL); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(gtk_main_quit), NULL); else - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(gtk_widget_hide), obj2); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(gtk_widget_hide), obj2); } else if ((suffix = strstr(w_id, "_apply")) != NULL && GTK_IS_FILE_CHOOSER_DIALOG(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) - g_signal_connect_swapped(obj, "clicked", - G_CALLBACK(cb_send_file_chooser_dialog_selection), - info_obj_new(o, obj2, NULL)); + sig_conn_swapped(obj, "clicked", + G_CALLBACK(cb_send_file_chooser_dialog_selection), + info_obj_new(o, obj2, NULL)); } else if (GTK_IS_MENU_ITEM(obj)) if ((suffix = strstr(w_id, "_invoke")) != NULL && GTK_IS_DIALOG(obj2 = obj_sans_suffix(ar->builder, suffix, w_id))) - g_signal_connect_swapped(obj, "activate", - G_CALLBACK(gtk_widget_show), obj2); + sig_conn_swapped(obj, "activate", + G_CALLBACK(gtk_widget_show), obj2); else - g_signal_connect(obj, "activate", - G_CALLBACK(cb_menu_item), info_txt_new(o, "active")); + sig_conn(obj, "activate", + G_CALLBACK(cb_menu_item), info_txt_new(o, "active")); else if (GTK_IS_WINDOW(obj)) { - g_signal_connect(obj, "delete-event", - G_CALLBACK(cb_event_simple), info_txt_new(o, "closed")); + sig_conn(obj, "delete-event", + G_CALLBACK(cb_event_simple), info_txt_new(o, "closed")); if (eql(w_id, MAIN_WIN)) - g_signal_connect_swapped(obj, "delete-event", - G_CALLBACK(gtk_main_quit), NULL); + sig_conn_swapped(obj, "delete-event", + G_CALLBACK(gtk_main_quit), NULL); else - g_signal_connect(obj, "delete-event", - G_CALLBACK(gtk_widget_hide_on_delete), NULL); + sig_conn(obj, "delete-event", + G_CALLBACK(gtk_widget_hide_on_delete), NULL); } else if (type == GTK_TYPE_FILE_CHOOSER_BUTTON) - g_signal_connect(obj, "file-set", - G_CALLBACK(cb_file_chooser_button), info_txt_new(o, "file")); + sig_conn(obj, "file-set", + G_CALLBACK(cb_file_chooser_button), info_txt_new(o, "file")); else if (type == GTK_TYPE_COLOR_BUTTON) - g_signal_connect(obj, "color-set", - G_CALLBACK(cb_color_button), info_txt_new(o, "color")); + sig_conn(obj, "color-set", + G_CALLBACK(cb_color_button), info_txt_new(o, "color")); else if (type == GTK_TYPE_FONT_BUTTON) - g_signal_connect(obj, "font-set", - G_CALLBACK(cb_font_button), info_txt_new(o, "font")); + sig_conn(obj, "font-set", + G_CALLBACK(cb_font_button), info_txt_new(o, "font")); else if (type == GTK_TYPE_SWITCH) - g_signal_connect(obj, "notify::active", - G_CALLBACK(cb_switch), info_txt_new(o, NULL)); + sig_conn(obj, "notify::active", + G_CALLBACK(cb_switch), info_txt_new(o, NULL)); else if (type == GTK_TYPE_TOGGLE_BUTTON || type == GTK_TYPE_RADIO_BUTTON || type == GTK_TYPE_CHECK_BUTTON) - g_signal_connect(obj, "toggled", - G_CALLBACK(cb_toggle_button), info_txt_new(o, NULL)); + sig_conn(obj, "toggled", + G_CALLBACK(cb_toggle_button), info_txt_new(o, NULL)); else if (type == GTK_TYPE_ENTRY) - g_signal_connect(obj, "changed", - G_CALLBACK(cb_editable), info_txt_new(o, "text")); + sig_conn(obj, "changed", + G_CALLBACK(cb_editable), info_txt_new(o, "text")); else if (type == GTK_TYPE_SPIN_BUTTON) - g_signal_connect(obj, "value_changed", - G_CALLBACK(cb_spin_button), info_txt_new(o, "text")); /* TODO: rename to "value" */ + sig_conn(obj, "value_changed", + G_CALLBACK(cb_spin_button), info_txt_new(o, "text")); /* TODO: rename to "value" */ else if (type == GTK_TYPE_SCALE) - g_signal_connect(obj, "value-changed", - G_CALLBACK(cb_range), info_txt_new(o, "value")); + sig_conn(obj, "value-changed", + G_CALLBACK(cb_range), info_txt_new(o, "value")); else if (type == GTK_TYPE_CALENDAR) { - g_signal_connect(obj, "day-selected-double-click", - G_CALLBACK(cb_calendar), info_txt_new(o, "doubleclicked")); - g_signal_connect(obj, "day-selected", - G_CALLBACK(cb_calendar), info_txt_new(o, "clicked")); + sig_conn(obj, "day-selected-double-click", + G_CALLBACK(cb_calendar), info_txt_new(o, "doubleclicked")); + sig_conn(obj, "day-selected", + G_CALLBACK(cb_calendar), info_txt_new(o, "clicked")); } else if (type == GTK_TYPE_TREE_SELECTION) - g_signal_connect(obj, "changed", - G_CALLBACK(cb_tree_selection), info_txt_new(o, "clicked")); + sig_conn(obj, "changed", + G_CALLBACK(cb_tree_selection), info_txt_new(o, "clicked")); else if (type == GTK_TYPE_SOCKET) { - g_signal_connect(obj, "plug-added", - G_CALLBACK(cb_simple), info_txt_new(o, "plug-added")); - g_signal_connect(obj, "plug-removed", - G_CALLBACK(cb_simple), info_txt_new(o, "plug-removed")); + sig_conn(obj, "plug-added", + G_CALLBACK(cb_simple), info_txt_new(o, "plug-added")); + sig_conn(obj, "plug-removed", + G_CALLBACK(cb_simple), info_txt_new(o, "plug-removed")); /* TODO: rename to plug_added, plug_removed */ } else if (type == GTK_TYPE_DRAWING_AREA) - g_signal_connect(obj, "draw", G_CALLBACK(cb_draw), NULL); + sig_conn(obj, "draw", G_CALLBACK(cb_draw), NULL); else if (type == GTK_TYPE_EVENT_BOX) { gtk_widget_set_can_focus(GTK_WIDGET(obj), true); - g_signal_connect(obj, "button-press-event", - G_CALLBACK(cb_event_box_button), - info_txt_new(o, "button_press")); - g_signal_connect(obj, "button-release-event", - G_CALLBACK(cb_event_box_button), - info_txt_new(o, "button_release")); - g_signal_connect(obj, "motion-notify-event", - G_CALLBACK(cb_event_box_motion), - info_txt_new(o, "motion")); - g_signal_connect(obj, "key-press-event", - G_CALLBACK(cb_event_box_key), - info_txt_new(o, "key_press")); + sig_conn(obj, "button-press-event", + G_CALLBACK(cb_event_box_button), + info_txt_new(o, "button_press")); + sig_conn(obj, "button-release-event", + G_CALLBACK(cb_event_box_button), + info_txt_new(o, "button_release")); + sig_conn(obj, "motion-notify-event", + G_CALLBACK(cb_event_box_motion), + info_txt_new(o, "motion")); + sig_conn(obj, "key-press-event", + G_CALLBACK(cb_event_box_key), + info_txt_new(o, "key_press")); } } diff --git a/pipeglade.ui b/pipeglade.ui index 1155f9b..70d148f 100644 --- a/pipeglade.ui +++ b/pipeglade.ui @@ -729,7 +729,7 @@ selection liststore1 0 - + multiple @@ -910,7 +910,7 @@ selection 2 True - + multiple @@ -1069,7 +1069,7 @@ selection 2 True - + multiple diff --git a/pipegladetest.sh b/pipegladetest.sh index d15677c..eb55978 100755 --- a/pipegladetest.sh +++ b/pipegladetest.sh @@ -293,6 +293,8 @@ if test $AUTOMATIC; then "ignoring command \"nnn:style font:Bold 11\"" check_error "nnn:force" \ "ignoring command \"nnn:force\"" + check_error "nnn:block 1" \ + "ignoring command \"nnn:block 1\"" # Wrong number or kind of arguments for generic actions check_error "button1:set_sensitive" \ "ignoring GtkButton command \"button1:set_sensitive\"" @@ -324,6 +326,10 @@ if test $AUTOMATIC; then "ignoring GtkButton command \"button1:force 2\"" check_error "_:main_quit 2" \ "ignoring command \"_:main_quit 2\"" + check_error "button1:block 2" \ + "ignoring GtkButton command \"button1:block 2\"" + check_error "button1:block 0 0" \ + "ignoring GtkButton command \"button1:block 0 0\"" # Widget that shouldn't fire callbacks check_error "label1:force" \ "ignoring GtkLabel command \"label1:force\"" @@ -1711,6 +1717,9 @@ if test $AUTOMATIC; then "entry1:set_text FFFF" \ "entry1:text FFFF" check 1 "" \ + "entry1:block 1\n entry1:set_text XXXXX\n entry1:block 0\n entry1:set_text EEEE" \ + "entry1:text EEEE" + check 1 "" \ "entry1:set_text $BIG_STRING" \ "entry1:text $BIG_STRING" check 1 "" \ @@ -1733,23 +1742,25 @@ if test $AUTOMATIC; then check 1 "" \ "entry1:force" \ "entry1:text GGGG" + check 1 "" \ + "spinbutton1:block 1\n spinbutton1:set_text 29.0\n spinbutton1:block 0\n spinbutton1:set_text 28.0\n" \ + "spinbutton1:text 28.0" check 2 "" \ "spinbutton1:set_text 33.0\n spinbutton1:set_range 50 60\n" \ "spinbutton1:text 33.0" \ "spinbutton1:text 50.0" - check 2 "" \ - "radiobutton2:set_active 1" \ - "radiobutton1:0" \ - "radiobutton2:1" + check 1 "" \ + "radiobutton2:block 1\n radiobutton2:set_active 1\n radiobutton2:block 0" \ + "radiobutton1:0" check 2 "" \ "radiobutton1:set_active 1" \ "radiobutton2:0" \ "radiobutton1:1" check 1 "" \ - "switch1:set_active 1" \ + "switch1:set_active 1\n switch1:block 1\n switch1:set_active 0" \ "switch1:1" check 1 "" \ - "switch1:set_active 0" \ + "switch1:set_active 1\n switch1:block 0\n switch1:set_active 0" \ "switch1:0" check 0 "" \ "progressbar1:set_text $BIG_STRING" @@ -1762,6 +1773,12 @@ check 1 "" \ "togglebutton1:set_active 1" \ "togglebutton1:1" check 1 "" \ + "togglebutton1:block 1\n togglebutton1:set_active 0\n togglebutton1:block 0\n togglebutton1:set_active 1" \ + "togglebutton1:1" +check 1 "" \ + "calendar1:block 1\n calendar1:select_date 1752-05-17\n calendar1:block 0\n calendar1:select_date 1752-05-18" \ + "calendar1:clicked 1752-05-18" +check 1 "" \ "calendar1:select_date 1752-03-29" \ "calendar1:clicked 1752-03-29" @@ -2277,7 +2294,7 @@ if test $AUTOMATIC; then "scale1:value 10.000000" \ "scale1:value 10.000000" check 2 "" \ - "scale1:set_range 20 22\n scale1:set_value 10\n scale1:set_value 100" \ + "scale1:block 1\n scale1:set_range 20 22\n scale1:set_value 21\n scale1:block 0\n scale1:set_value 10\n scale1:set_value 100" \ "scale1:value 20.000000" \ "scale1:value 22.000000" check 1 "" \ -- 2.11.4.GIT