Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gtk displaying a dialog from within a dialog

Tags:

c

linux

dialog

gtk

I'm writing a gtk program, at some point in my application, I call a gtkdialog to obtain input from the user. what I want to do is close the current dialog and open another dialog when the OK button is clicked, I already have a function doing some other work, starting the dialog in that function would be great. here's the parent dialog code:

GtkWidget * new_button_dialog = gtk_dialog_new_with_buttons("new button",(GtkWindow *)container,GTK_DIALOG_DESTROY_WITH_PARENT,"OK", GTK_RESPONSE_OK,NULL);
       GtkWidget * content_area = gtk_dialog_get_content_area (GTK_DIALOG (new_button_dialog));
       GtkWidget * button_name_label = gtk_label_new ("Press escape at any time to cancel.\ntype in the button name");
       gtk_container_add (GTK_CONTAINER (content_area) , button_name_label);
       GtkWidget * button_name_entry = gtk_entry_new();
       gtk_entry_set_activates_default((GtkEntry*)button_name_entry,TRUE);
       gtk_container_add(GTK_CONTAINER(content_area), button_name_entry);
       GtkWidget * button_text_dialog = gtk_dialog_new_with_buttons("new button",(GtkWindow *)container,GTK_DIALOG_DESTROY_WITH_PARENT,"OK", GTK_RESPONSE_OK,NULL);
      gtk_container_add (GTK_CONTAINER (container), button);
       g_signal_connect(new_button_dialog,"response",G_CALLBACK (add_new_button),NULL);
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",GTK_SIGNAL_FUNC (gtk_widget_show_all),(GtkWidget*)new_button_dialog);
       gtk_widget_show_all((GtkWidget *)container);
like image 836
Adel Ahmed Avatar asked Oct 20 '15 21:10

Adel Ahmed


1 Answers

The response of a dialog cannot be handled in the same function that the dialog is created. It means that in one function you cannot create a dialog, handle its input, close that dialog and open a new dialog. It cannot be done since when that function exits the dialog entry is empty and the first dialog is just shown.

It should be done as a chain of separate event handlers. All needed variables (data context) should be transferred between those separate handlers using a pointer to structure that holds that context.

The below full runnable example demonstrates such flow.

In the fist dialog handler we want to do something with the main window and some container object and maybe we want some other data that is shared between widgets of that main window. The structure window_ctx_t is declared for such context. There is a boolean flag in that context to lock creation on new dialog chains before closing active dialogs.

The function first_dialog_create creates the first dialog with text entry field. It receives window_ctx_t, where the main window pointer is stored that is used for the dialog creation. Now we need to attach to that dialog response handler and provide to the handler enough data for its functionality. In that example we provide pointer to text entry that is used to read text.

Using provided data the response handler add_new_button is able to read text entry, to create a new dialog attached to the main window and also to do something with provided container.

When the first dialog data context is not needed it is freed by calling g_free() in the dialog destroyed handler.

Build the example:

gcc -Wall -g dialogs.c -o dialogs `pkg-config --cflags gtk+-2.0` `pkg-config --libs gtk+-2.0`

Example "dialogs.c"

#include <gtk/gtk.h>

typedef struct {
    GtkWidget *container;
    GtkWindow *window;
    gboolean dialogs_created;
} window_ctx_t;

typedef struct {
    GtkEntry *entry;
    window_ctx_t *window_ctx;
} first_dialog_data_t;

static void first_dialog_create(gpointer context);

static void another_dialog_response(GtkWidget *dialog, gint response_id,
    gpointer ctx)
{
    window_ctx_t *win_ctx = ctx;

    gtk_widget_destroy(dialog);
    win_ctx->dialogs_created = FALSE;
}

static void cleanup_first_dialog(GtkWidget *widget, gpointer data)
{
    g_free(data);
}

/* response callback of the first dialog */
static void add_new_button(GtkWidget *dialog, gint response_id,
    gpointer ctx)
{
    GtkWidget *button;
    GtkWidget *button_text_dialog;
    first_dialog_data_t *data = ctx;

    if (response_id != GTK_RESPONSE_OK) {
        data->window_ctx->dialogs_created = FALSE;
        goto Exit;
    }

    /* Create a new button with the label from text entry */
    button = gtk_button_new_with_label(gtk_entry_get_text(data->entry));
    /* When the button receives the "clicked" signal, it will again open
     * "new button" dialog */
    g_signal_connect_swapped(button, "clicked", G_CALLBACK(first_dialog_create),
        data->window_ctx);
    /* add button to the container */
    gtk_container_add(GTK_CONTAINER(data->window_ctx->container), button);
    /* show button */
    gtk_widget_show(button);

    /* create another dialog */
    button_text_dialog = gtk_dialog_new_with_buttons("new button",
        data->window_ctx->window, GTK_DIALOG_DESTROY_WITH_PARENT, "OK",
        GTK_RESPONSE_OK, NULL);
    g_signal_connect(button_text_dialog, "response",
        G_CALLBACK(another_dialog_response), (gpointer)data->window_ctx);

    gtk_widget_show_all(button_text_dialog);

Exit:
    gtk_widget_destroy(dialog);
}

static void first_dialog_create(gpointer context)
{
    first_dialog_data_t *data;
    window_ctx_t *win_ctx = context;

    if (win_ctx->dialogs_created)
        return;

    /* lock buttons handler */
    win_ctx->dialogs_created = TRUE;

    /* allocate structure for dialog context */
    data = g_new0(first_dialog_data_t, 1);

    /* create first dialog, its label and text entry */
    GtkWidget *new_button_dialog = gtk_dialog_new_with_buttons("new button",
        win_ctx->window, GTK_DIALOG_DESTROY_WITH_PARENT,"OK",
        GTK_RESPONSE_OK, NULL);

    GtkWidget *content_area = gtk_dialog_get_content_area(
    GTK_DIALOG(new_button_dialog));
    GtkWidget *button_name_label = gtk_label_new("Press escape at any time to "
        "cancel.\ntype in the button name");
    gtk_container_add(GTK_CONTAINER(content_area), button_name_label);

    GtkWidget * button_name_entry = gtk_entry_new();
    gtk_entry_set_activates_default((GtkEntry*)button_name_entry,TRUE);
    gtk_container_add(GTK_CONTAINER(content_area), button_name_entry);

    /* provide data to response handler */
    data->entry = (GtkEntry *)button_name_entry;
    data->window_ctx = win_ctx;
    g_signal_connect(new_button_dialog, "response", G_CALLBACK(add_new_button),
        data);

    /* handler to free dialog context 'data' when destroed */
    g_signal_connect(new_button_dialog, "destroy",
        G_CALLBACK(cleanup_first_dialog), data);

    /* show first dialog */
    gtk_widget_show_all(new_button_dialog);
}

static void destroy(GtkWidget *widget, gpointer data)
{
    gtk_main_quit();
}

int main(int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *box1;
    GtkWidget *button;
    window_ctx_t win_ctx;

    gtk_init (&argc, &argv);
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    box1 = gtk_hbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(window), box1);

    /* set window context */
    win_ctx.window = (GtkWindow *)window;
    win_ctx.container = box1;
    win_ctx.dialogs_created = FALSE;

    g_signal_connect (window, "destroy", G_CALLBACK(destroy), NULL);
    gtk_container_set_border_width(GTK_CONTAINER(window), 10);

    button = gtk_button_new_with_label("Start");
    g_signal_connect_swapped(button, "clicked", G_CALLBACK(first_dialog_create),
        &win_ctx);
    gtk_container_add(GTK_CONTAINER(box1), button);

    gtk_widget_show_all(window);

    gtk_main();

    return 0;
}
like image 92
Orest Hera Avatar answered Nov 06 '22 03:11

Orest Hera