tags:

views:

1851

answers:

5

I have been trying to implement Win32's MessageBox using GTK. The app using SDL/OpenGL, so this isn't a GTK app.

I handle the initialisation (gtk_init) sort of stuff inside the MessageBox function as follows:

int MessageBox(HWND hwnd, const char* text, const char* caption, UINT type)
{
GtkWidget *window = NULL;
GtkWidget *dialog = NULL;

gtk_init(&gtkArgc, &gtkArgv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
// gcallback calls gtk_main_quit()
gtk_init_add((GtkFunction)gcallback, NULL);

if (type & MB_YESNO) {
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, text);
} else {
dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, text);
}

gtk_window_set_title(GTK_WINDOW(dialog), caption);
gint result = gtk_dialog_run(GTK_DIALOG(dialog));

gtk_main();

gtk_widget_destroy(dialog);

if (type & MB_YESNO) {
switch (result) {
default:
case GTK_RESPONSE_DELETE_EVENT:
case GTK_RESPONSE_NO:
return IDNO;
break;
case GTK_RESPONSE_YES:
return IDYES;
break;
}
}

return IDOK;
}

Now, I am by no means an experienced GTK programmer, and I realise that I'm probably doing something(s) horribly wrong.

However, my problem is that the last dialog popped up with this function stays around until the process exits. Any ideas?

+2  A: 

A few things:

You are creating (and not using) an unnecessary toplevel window, named window. You can just delete these lines:

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), NULL);
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);

Also, the flow doesn't seem quite right. gtk_main() starts the GTK main loop, which blocks until something exits it. gtk_dialog_run() also starts a main loop, but it exits as soon as one of the buttons is clicked.

I think it might be enough for you to remove the gtk_init_add() and gtk_main() calls, and simply deal with the return value. Also the gtk_widget_destroy() call is unnecessary, as the dialog window is automatically destroyed when gtk_dialog_run() returns.

Joe Shaw
A: 

@Joe:
Thanks for the response. My code is a lot more readable now, but the behaviour is identical to before. I also moved gtk_init to be called once (no idea why I put it in the function itself), still nothing. The dialog stops redrawing itself, so it's obviously destroyed, yet it doesn't actually stop displaying until the program exits.

Alternatively, a non-modal dialog seems to work - but I can't tell how to get the response out of it.

Bernard
+3  A: 

Hmm, ok. I'd suggest code like this, then:

typedef struct {
int type;
int result;
} DialogData;

static gboolean
display_dialog(gpointer user_data)
{
DialogData *dialog_data = user_data;
GtkWidget *dialog;

if (dialog_data->type & MB_YESNO)
dialog = gtk_message_dialog_new(...);
else
dialog = gtk_message_dialog_new(...);

// Set title, etc.

dialog_data->result = gtk_dialog_run(...);

gtk_main_quit(); // Quits the main loop run in MessageBox()

return FALSE;
}

int MessageBox(...)
{
DialogData dialog_data;

dialog_data.type = type;

gtk_idle_add(display_dialog, &dialog_data);

gtk_main();

// Do stuff based on dialog_data.result
}

The struct is because you need to pass around a couple pieces of data. The gtk_idle_add() call adds a method to be run when the main loop is running and idle, and the FALSE return value from the display_dialog() call means that it's only run once. After we get the result from the dialog, we quit the main loop. That'll cause the gtk_main() in your main MessageBox() method to return, and you'll be able to access the result from there.

Hope this helps!

Joe Shaw
A: 

@Joe:
Unfortunately, the problem still persists. Some behavours I have noticed:

  • If MessageBox is called more than once, the latest dialog will cause the old dialog to dissapear.

  • Occasionally when turning off the computer, I will see an unthemed version of the dialog despite there being no instances of the program running.

  • Once, when I was trying to run a completely unrelated program, the dialog appeared insted of the application I wanted starting.

EDIT:

Calling gtk_widget_unref(dialog) causes it to work as expected (:D). However, GTK+ emits all kinds of warnings:

GLib-GObject-WARNING **: instance with invalid (NULL) class pointer
GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed

Both of these occur twice. Now from my limited understanding of this toolkit, gtk_widget_unref subtracts one reference to an object. Because I have to call this, does this mean there is some kind of dangling reference hanging around?

Bernard
+1  A: 

To manage a dialog box with GTK+, use a GtkDialog and gtk_dialog_run() instead of managing a window and a main loop by yourself.

EDIT / ADDENDUM :

What I mean is "just use" : I don't understand why you create a windows you never use and a main loop which seems useless (at least from the piece of code you posted). You can write something as short as :

int MessageBox(HWND hwnd, const char* text, const char* caption, UINT type)
{
    GtkWidget *dialog ;

    /* Instead of 0, use GTK_DIALOG_MODAL to get a modal dialog box */

    if (type & MB_YESNO)
        dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, text );
    else
        dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, text );


    gtk_window_set_title(GTK_WINDOW(dialog), caption);
    gint result = gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy( GTK_WIDGET(dialog) );

    if (type & MB_YESNO)
    {
        switch (result)
        {
        default:
        case GTK_RESPONSE_DELETE_EVENT:
        case GTK_RESPONSE_NO:
            return IDNO;
        case GTK_RESPONSE_YES:
            return IDYES;
        }
        return IDOK;
    } 
}
Platypus