3

I am having trouble figuring out how to write signal handlers in my C code for the message dialogs that I am creating in GLADE. If I were not using GLADE, the signal handler would include the necessary information to construct the message dialog itself. For example, an "are you sure you want to quit?" message dialog would have the form:

void show_question(GtkWidget *widget, gpointer window) {

  GtkWidget *dialog;
  dialog = gtk_message_dialog_new(GTK_WINDOW(window),
          GTK_DIALOG_DESTROY_WITH_PARENT,
          GTK_MESSAGE_QUESTION,
          GTK_BUTTONS_YES_NO,
          "Are you sure to quit?");
  gtk_window_set_title(GTK_WINDOW(dialog), "Question");
  gtk_dialog_run(GTK_DIALOG(dialog));
  gtk_widget_destroy(dialog);
}

Likewise, an "about window" would have the form:

void show_about(GtkWidget *widget, gpointer data) {

  GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("battery.png", NULL);

  GtkWidget *dialog = gtk_about_dialog_new();
  gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), "Battery");
  gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), "0.9"); 
  gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "(c) Jan Bodnar");
  gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "Battery is a simple tool for battery checking.");
  gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://www.batteryhq.net");
  gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(dialog), pixbuf);
  g_object_unref(pixbuf), pixbuf = NULL;
  gtk_dialog_run(GTK_DIALOG (dialog));
  gtk_widget_destroy(dialog);
}

Now that I am fully creating these windows within GLADE, I am not sure what form the signal handler should take (in C) in order to connect, say, clicking Help -> About to the about window created in GLADE, or connecting clicking File -> Quit with the "are you sure you want to quit" message dialog created in GLADE. I am new to both GTK+ and Glade and I can't seem to find anything that has been of use in resolving this. Any help would be greatly appreciated.

EDIT: Below is an example glade XML file that consists of two top level windows. The first is the main window with a single box containing a menu bar. The second is an about dialog. What I am looking for is help in writing the signal handler that will open the about window after clicking File -> About in the menu bar.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.12"/>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <child>
      <object class="GtkBox" id="box1">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar" id="menubar1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem" id="menuitem1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_File</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem1">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                       <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                     <object class="GtkImageMenuItem" id="imagemenuitem2">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem3">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem4">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem" id="separatormenuitem1">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem5">
                        <property name="label">gtk-quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem2">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Edit</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu2">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem6">
                        <property name="label">gtk-cut</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem7">
                        <property name="label">gtk-copy</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                     <object class="GtkImageMenuItem" id="imagemenuitem8">
                        <property name="label">gtk-paste</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem9">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
           <child>
              <object class="GtkMenuItem" id="menuitem3">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_View</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem" id="menuitem4">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">_Help</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu" id="menu3">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem" id="imagemenuitem10">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                       <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
  <object class="GtkAboutDialog" id="window_about">
    <property name="can_focus">False</property>
    <property name="type_hint">dialog</property>
    <property name="program_name">Glade</property>
    <property name="logo_icon_name">image-missing</property>
    <child internal-child="vbox">
      <object class="GtkBox" id="aboutdialog-vbox1">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox" id="aboutdialog-action_area1">
            <property name="can_focus">False</property>
            <property name="layout_style">end</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <placeholder/>
        </child>
      </object>
    </child>
  </object>
</interface>
Leigh K
  • 561
  • 6
  • 20
  • You're not planning on trying to display things like message dialogs from within a signal handler, are you? The **only** functions that can safely be called from within a signal handler are [async-signal-safe functions](https://stackoverflow.com/questions/8493095/what-constitutes-asynchronous-safeness). – Andrew Henle Jan 09 '18 at 22:16
  • I'm not *trying* to do that. It seems as though that is what is happening with the functions defined above. I'm just looking for clarification on how the signal handlers should be constructed given the Glade XML file having predefined the message dialog windows. – Leigh K Jan 10 '18 at 00:12

2 Answers2

4

If understood correctly, instead of creating the dialogs programmatically, you want to create the dialogs via glade.

By using glade you must use GtkBuilder.

Let's say you create a simple Yes, No dialog with glade (yesno_dialog.ui):

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkDialog" id="dialog1">
    <property name="can_focus">False</property>
    <property name="modal">True</property>
    <property name="default_width">275</property>
    <property name="default_height">130</property>
    <property name="type_hint">dialog</property>
    <child internal-child="vbox">
      <object class="GtkBox">
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <property name="spacing">2</property>
        <child internal-child="action_area">
          <object class="GtkButtonBox">
            <property name="can_focus">False</property>
            <property name="hexpand">True</property>
            <property name="layout_style">spread</property>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label">gtk-yes</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label">gtk-no</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="use_stock">True</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkLabel">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="halign">center</property>
            <property name="valign">center</property>
            <property name="hexpand">True</property>
            <property name="vexpand">True</property>
            <property name="label" translatable="yes">Are you sure to quit?</property>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
    <action-widgets>
      <action-widget response="1">button1</action-widget>
      <action-widget response="2">button2</action-widget>
    </action-widgets>
    <child>
      <placeholder/>
    </child>
  </object>
</interface>

Which should look like this:

enter image description here

Notice the response_id as 1 for later reference.

Let's test a simple window with a button which will trigger a dialog via its clicked signal handler / callback:

#include <gtk/gtk.h>

void on_button_clicked (GtkButton *button, gpointer user_data) {
   int response;
   GtkWidget  *dialog;
   GtkBuilder *builder;

   g_return_if_fail (user_data != NULL);

   builder = gtk_builder_new_from_file("yesno_dialog.ui");

   dialog = GTK_WIDGET(gtk_builder_get_object (builder, "dialog1"));
   gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(user_data));

   gtk_widget_show_all(dialog);

   response = gtk_dialog_run(GTK_DIALOG(dialog));

   gtk_widget_destroy(dialog);

   g_object_unref(G_OBJECT(builder));

   g_print ("Response is %s\n", response == 1 ? "Yes" : "No");
}


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

   gtk_init(&argc,&argv);

   button = gtk_button_new_with_label("Press for dialog");
   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

   gtk_container_add(GTK_CONTAINER(window), button);

   g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(on_button_clicked), window);
   g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

   gtk_widget_show_all(window);

   gtk_main();

   return 0;
}

When you construct a dialog programmatically with standard response ids then you can use the predefined enumerators to check the response. Using glade, even though we've defined Stock buttons, we must define (user-defined) response ids so that the gtk_dialog_run functions understands that the user has chosen a response.

Compiling with:

gcc -o dialog main.c `pkg-config --cflags --libs gtk+-3.0`

Should result in something similar to this:

enter image description here

José Fonte
  • 4,016
  • 2
  • 16
  • 28
  • Jose, I appreciate the thorough response, however this did not answer the original question. Please see the edit to the original question for clarification. Thanks! – Leigh K Jan 10 '18 at 02:04
  • @LeighK The example can be applied to your goal. The basic step here is to retrieve the auto-instantiated Widgets from the Glade XML definition via GtkBuilder. The GtkBuilder function, get_object, allows you to get the glade widgets by name. So in your callback, you can pass the builder object or the dialog. Later i can try to complement my answer. – José Fonte Jan 10 '18 at 10:56
  • Jose, I just ran your example and it seems that you are indeed correct. However, there are some differences. First, I am used to seeing the GtkBuilder being referenced in main. You do not reference it in main, but rather within the "on_button_clicked" signal handler (and then unreference it). I imagine I can reference it both in main and within the signal handler I am interested in writing though? – Leigh K Jan 10 '18 at 14:59
  • The biggest issue is that your "on_button_clicked" signal handler is being fed a pointer to the GtkButton you created in main. In my case, it needs to be linked to the GtkImageMenuItem "gtk-about." I am not sure how this would be done. Aside from that caveat, I suspect the signal handler you have written would do the job. – Leigh K Jan 10 '18 at 14:59
  • @LeighK the button clicked event, callback, signal handler (name it as you like), has a prototype/signature which is void *user_function (GtkButton *button, gpointer user_data). When you connect a function to handle that event it must follow that convention. button is the widget that triggered the event. user_data is the place for you to pass more arguments, be it simple or more, encapsulated in a structure, object as e.g. Regarding GtkBuilder, the instantiated widgets are destroyed when you destroy them programmatically. If i passed builder, then i could not destroy the dialog but hide it – José Fonte Jan 10 '18 at 15:06
  • @LeighK g_object_unref (builder) its a good rule so that you destroy unnecessary widgets/objects inside builder but there are situations where you keep builder for later use. – José Fonte Jan 10 '18 at 15:09
  • @LeighK you must check which signals are emitted by the menu widget and check that prototype but as a rule, the first callback argument its the widget/object that triggered it. Edit: the prototype is similar but as said, the first argument is the menuItem, check the "activate" signal : https://developer.gnome.org/gtk3/stable/GtkMenuItem.html#GtkMenuItem-activate – José Fonte Jan 10 '18 at 15:13
  • Jose, I appreciate the assistance here but unfortunately I am not making much progress (and this seems like it should be a simple thing to do). The GtkImageMenuItem for "Quit" has an activate signal handler that I named "file_quit_clicked" and all I need that to do is open up the GtkMessageDialog "quit_dialog" that is entirely defined within the Glade file. I got something to work using your outline above but the GtkButtons in the dialog window trigger the "g_return_if_fail" function, and what I have seems way too convoluted for something that should seemingly be simple and straightforward. – Leigh K Jan 10 '18 at 17:19
  • 2
    @LeighK It's straightforward. You simply must understand the "Gtk" framework. Care to join the Gtk chat so that we can try and aid you, by fully understand the problem and elaborate on the answer. Chat: https://chat.stackoverflow.com/rooms/160557/gtk – José Fonte Jan 10 '18 at 17:32
2

After discussions with @Jose Fonte, the solution to my question is the following:

// Signal Handler for Clicking File -> Quit
void file_quit_clicked (__attribute__((unused)) GtkMenuItem *item, gpointer user_data) 
{
    GtkBuilder *builder = (GtkBuilder *) user_data;

    GtkDialog *dialog = GTK_DIALOG(gtk_builder_get_object(builder, "quit_dialog"));

    gtk_widget_show_all(GTK_WIDGET(dialog));

    // This switch statement and gtk_widget_hide is needed so that the dialog window can be closed 
    // and reopened again without causing errors (i.e. if the user clicks "no" when asked "are you
    // sure you want to quit." Without these, the window errors out the second time opened.
    gint result = gtk_dialog_run (dialog);
    switch (result)
    {
        case GTK_RESPONSE_ACCEPT:
           // do_application_specific_something (); - Nothing Required
           break;
        default:
           // do_nothing_since_dialog_was_cancelled (); - Nothing Required
           break;
    }
    gtk_widget_hide(GTK_WIDGET(dialog));

}

with the following snippet inserted into the main function:

    // So the "Are you sure you want to quit?" Dialog Box is hidden after clicking "x"
    GtkDialog *quit_dialog = GTK_DIALOG(gtk_builder_get_object(builder, "quit_dialog"));
    gtk_widget_hide_on_delete (GTK_WIDGET(quit_dialog));
José Fonte
  • 4,016
  • 2
  • 16
  • 28
Leigh K
  • 561
  • 6
  • 20
  • 1
    `gint result = gtk_dialog_run (GTK_DIALOG (dialog));` does not need the `GTK_DIALOG()` macro as it was defined as a GtkDialog. I'll edit the answer to reflect that. – José Fonte Jan 11 '18 at 11:09
  • 2
    Good deal Jose. Many thanks for all of your assistance with working out the solution! – Leigh K Jan 11 '18 at 18:14