From: John Darrington Date: Sun, 7 Oct 2007 00:25:03 +0000 (+0000) Subject: Added feature to make OK and PASTE buttons insensitive until the dialog box X-Git-Tag: v0.6.0~232 X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6063e91bfb952c369ca3343928ed30f53d1f0bdd;p=pspp-builds.git Added feature to make OK and PASTE buttons insensitive until the dialog box contains valid data. --- diff --git a/src/ui/gui/ChangeLog b/src/ui/gui/ChangeLog index 74fc10fe..c0c4d283 100644 --- a/src/ui/gui/ChangeLog +++ b/src/ui/gui/ChangeLog @@ -1,3 +1,17 @@ +2007-10-06 John Darrington + + * psppire-dialog.c psppire-dialog.h: Added a predicate function + member to indicate when a dialog's state is (not) valid. Added a + signal "validity-changed" which gets emitted whenever this + predicate changes. + + * psppire-buttonbox.c: Connect to the toplevel window's + "validity-changed" signal (assuming it happens to be a + PsppireDialog) and set the OK, PASTE, GOTO and CONTINUE buttons + according.y. + + * descriptives-dialog.c compute-dialog.c: Add a validity predicate. + 2007-10-04 John Darrington * compute-dialog.c goto-case-dialog.c main.c psppire-keypad.c: Added diff --git a/src/ui/gui/compute-dialog.c b/src/ui/gui/compute-dialog.c index 9d3e0b46..3386680d 100644 --- a/src/ui/gui/compute-dialog.c +++ b/src/ui/gui/compute-dialog.c @@ -327,6 +327,28 @@ on_expression_toggle (GtkToggleButton *button, gpointer data) } } + +/* Return TRUE if the dialog box's widgets' state are such that clicking OK + might not result in erroneous syntax being generated */ +static gboolean +contents_plausible (gpointer data) +{ + struct compute_dialog *cd = data; + + GtkWidget *target = get_widget_assert (cd->xml, "compute-entry1"); + GtkWidget *syntax_area = get_widget_assert (cd->xml, "compute-textview1"); + GtkTextBuffer *buffer = + gtk_text_view_get_buffer (GTK_TEXT_VIEW (syntax_area)); + + if ( 0 == strcmp ("", gtk_entry_get_text (GTK_ENTRY (target)))) + return FALSE; + + if ( gtk_text_buffer_get_char_count (buffer) == 0 ) + return FALSE; + + return TRUE; +} + /* Pops up the Compute dialog box */ void compute_dialog (GObject *o, gpointer data) @@ -359,6 +381,7 @@ compute_dialog (GObject *o, gpointer data) vs = PSPPIRE_VAR_STORE (gtk_sheet_get_model (var_sheet)); + scd.dict = vs->dict; scd.use_type = FALSE; @@ -389,6 +412,9 @@ compute_dialog (GObject *o, gpointer data) scd.xml = xml; + psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog), + contents_plausible, &scd); + g_signal_connect (target, "changed", G_CALLBACK (on_target_change), &scd); g_signal_connect (dialog, "refresh", G_CALLBACK (refresh), &scd); diff --git a/src/ui/gui/descriptives-dialog.c b/src/ui/gui/descriptives-dialog.c index 5ed92d9d..5a9a2b73 100644 --- a/src/ui/gui/descriptives-dialog.c +++ b/src/ui/gui/descriptives-dialog.c @@ -287,6 +287,20 @@ put_statistics_in_treeview (GtkTreeView *treeview) gtk_tree_view_append_column (treeview, col); } + +/* Dialog is valid iff at least one variable has been selected */ +static gboolean +dialog_state_valid (gpointer data) +{ + struct descriptives_dialog *dd = data; + + GtkTreeModel *vars = gtk_tree_view_get_model (dd->stat_vars); + + GtkTreeIter notused; + + return gtk_tree_model_get_iter_first (vars, ¬used); +} + /* Pops up the Descriptives dialog box */ void descriptives_dialog (GObject *o, gpointer data) @@ -340,6 +354,9 @@ descriptives_dialog (GObject *o, gpointer data) g_signal_connect (dialog, "refresh", G_CALLBACK (refresh), &scd); + psppire_dialog_set_valid_predicate (PSPPIRE_DIALOG (dialog), + dialog_state_valid, &scd); + response = psppire_dialog_run (PSPPIRE_DIALOG (dialog)); diff --git a/src/ui/gui/psppire-buttonbox.c b/src/ui/gui/psppire-buttonbox.c index da39e086..45522107 100644 --- a/src/ui/gui/psppire-buttonbox.c +++ b/src/ui/gui/psppire-buttonbox.c @@ -225,6 +225,31 @@ refresh_clicked (GtkWidget *w, gpointer data) } + +static void +on_validity_change (GtkWidget *toplevel, gboolean valid, gpointer data) +{ + PsppireButtonBox *bb = data; + + /* Set the sensitivity of all the 'executive order' buttons */ + gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_OK]), valid); + gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_PASTE]), valid); + gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_GOTO]), valid); + gtk_widget_set_sensitive (GTK_WIDGET (bb->button[PSPPIRE_BUTTON_CONTINUE]), valid); +} + +static void +on_realize (GtkWidget *buttonbox, gpointer data) +{ + GtkWidget *toplevel = gtk_widget_get_toplevel (buttonbox); + + if ( PSPPIRE_IS_DIALOG (toplevel)) + { + g_signal_connect (toplevel, "validity-changed", + G_CALLBACK (on_validity_change), buttonbox); + } +} + static void psppire_button_box_init (PsppireButtonBox *bb) { @@ -299,6 +324,8 @@ psppire_button_box_init (PsppireButtonBox *bb) g_value_unset (&value); } + + g_signal_connect (bb, "realize", G_CALLBACK (on_realize), NULL); } diff --git a/src/ui/gui/psppire-dialog.c b/src/ui/gui/psppire-dialog.c index d961c7dc..90080094 100644 --- a/src/ui/gui/psppire-dialog.c +++ b/src/ui/gui/psppire-dialog.c @@ -20,12 +20,15 @@ #include #include #include "psppire-dialog.h" +#include "psppire-buttonbox.h" +#include "psppire-selector.h" static void psppire_dialog_class_init (PsppireDialogClass *); static void psppire_dialog_init (PsppireDialog *); enum {DIALOG_REFRESH, + VALIDITY_CHANGED, n_SIGNALS}; static guint signals [n_SIGNALS]; @@ -193,6 +196,18 @@ psppire_dialog_class_init (PsppireDialogClass *class) 0); + signals [VALIDITY_CHANGED] = + g_signal_new ("validity-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, + G_TYPE_BOOLEAN); + + object_class->finalize = psppire_dialog_finalize; } @@ -227,6 +242,8 @@ psppire_dialog_init (PsppireDialog *dialog) { GValue value = {0}; dialog->box = NULL; + dialog->contents_are_valid = NULL; + dialog->validity_data = NULL; g_value_init (&value, orientation_spec->value_type); g_param_value_set_default (orientation_spec, &value); @@ -259,13 +276,121 @@ psppire_dialog_new (void) return GTK_WIDGET (dialog) ; } + +static void +notify_change (PsppireDialog *dialog) +{ + if ( dialog->contents_are_valid ) + { + gboolean valid = dialog->contents_are_valid (dialog->validity_data); + + g_signal_emit (dialog, signals [VALIDITY_CHANGED], 0, valid); + } +} + + +/* Descend the widget tree, connecting appropriate signals to the + notify_change callback */ +static void +connect_notify_signal (GtkWidget *w, gpointer data) +{ + PsppireDialog *dialog = data; + + if ( PSPPIRE_IS_BUTTONBOX (w)) + return; + + + + if ( GTK_IS_CONTAINER (w)) + { + gtk_container_foreach (GTK_CONTAINER (w), + connect_notify_signal, + dialog); + } + + + /* It's unfortunate that GTK+ doesn't have a generic + "user-modified-state-changed" signal. Instead, we have to try and + predict what widgets and signals are likely to exist in our dialogs. */ + + if ( GTK_IS_TOGGLE_BUTTON (w)) + { + g_signal_connect_swapped (w, "toggled", G_CALLBACK (notify_change), + dialog); + } + + if ( PSPPIRE_IS_SELECTOR (w)) + { + g_signal_connect_swapped (w, "selected", G_CALLBACK (notify_change), + dialog); + + g_signal_connect_swapped (w, "de-selected", G_CALLBACK (notify_change), + dialog); + } + + if ( GTK_IS_EDITABLE (w)) + { + g_signal_connect_swapped (w, "changed", G_CALLBACK (notify_change), + dialog); + } + + if ( GTK_IS_CELL_EDITABLE (w)) + { + g_signal_connect_swapped (w, "editing-done", G_CALLBACK (notify_change), + dialog); + } + + if ( GTK_IS_TEXT_VIEW (w)) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w)); + + g_signal_connect_swapped (buffer, "changed", G_CALLBACK (notify_change), + dialog); + } + + if ( GTK_IS_TREE_VIEW (w)) + { + gint i = 0; + GtkTreeView *tv = GTK_TREE_VIEW (w); + GtkTreeSelection *selection = + gtk_tree_view_get_selection (tv); + GtkTreeViewColumn *col; + + g_signal_connect_swapped (selection, "changed", + G_CALLBACK (notify_change), dialog); + + while ((col = gtk_tree_view_get_column (tv, i++))) + { + GList *renderers = gtk_tree_view_column_get_cell_renderers (col); + GList *start = renderers; + while (renderers) + { + if ( GTK_IS_CELL_RENDERER_TOGGLE (renderers->data)) + g_signal_connect_swapped (renderers->data, "toggled", + G_CALLBACK (notify_change), dialog); + renderers = renderers->next; + } + g_list_free (start); + } + } +} + + gint psppire_dialog_run (PsppireDialog *dialog) { + if ( dialog->contents_are_valid != NULL ) + gtk_container_foreach (GTK_CONTAINER (dialog->box), + connect_notify_signal, + dialog); + dialog->loop = g_main_loop_new (NULL, FALSE); gtk_widget_show (GTK_WIDGET (dialog)); + if ( dialog->contents_are_valid != NULL) + g_signal_emit (dialog, signals [VALIDITY_CHANGED], 0, FALSE); + g_signal_emit (dialog, signals [DIALOG_REFRESH], 0); g_main_loop_run (dialog->loop); @@ -302,3 +427,15 @@ psppire_orientation_get_type (void) } return etype; } + + +void +psppire_dialog_set_valid_predicate (PsppireDialog *dialog, + ContentsAreValid contents_are_valid, + gpointer data) +{ + dialog->contents_are_valid = contents_are_valid; + dialog->validity_data = data; +} + + diff --git a/src/ui/gui/psppire-dialog.h b/src/ui/gui/psppire-dialog.h index 7f064e94..bd51a4c4 100644 --- a/src/ui/gui/psppire-dialog.h +++ b/src/ui/gui/psppire-dialog.h @@ -40,6 +40,9 @@ G_BEGIN_DECLS typedef struct _PsppireDialog PsppireDialog; typedef struct _PsppireDialogClass PsppireDialogClass; +typedef gboolean (*ContentsAreValid) (gpointer); + + struct _PsppireDialog { GtkWindow window; @@ -48,6 +51,9 @@ struct _PsppireDialog /* Private */ GMainLoop *loop; gint response; + + ContentsAreValid contents_are_valid; + gpointer validity_data; }; struct _PsppireDialogClass @@ -55,11 +61,15 @@ struct _PsppireDialogClass GtkWindowClass parent_class; }; + GType psppire_dialog_get_type (void); GtkWidget* psppire_dialog_new (void); void psppire_dialog_reload (PsppireDialog *); void psppire_dialog_close (PsppireDialog *); gint psppire_dialog_run (PsppireDialog *); +void psppire_dialog_set_valid_predicate (PsppireDialog *, + ContentsAreValid, + gpointer ); GType psppire_orientation_get_type (void);