Previous Up Next

KPlug Glade Tutorial

In Callbacks.c: Linking the Handlers to the Backend for Geocenter

The following details some of the gtk calls needed to make the handlers work in callbacks.c. I don't pretend that it's outstanding coding, everyone has their own style. I've left out a lot of the pure coding warnings I'd have put in if this were a coding tutorial (ie cleanup variables before exitting, etc, etc). Look at the actual code for an idea of how the backend works.

After I finish making the interface detailed on the previous page, I build the source. Then I open a term window and run ./autogen.sh in the geocenter directory. I also compile, then move to the /src subdirectory and get to work in callbacks.c after removing the surplus of windows created in main.c.

 
Adding the Exit Calls

When I open the file, I'm faced with empty functions. They have declarations, and that's about it. First thing I do is make sure the program will exit properly when the quit icon is clicked in the upper right hand corner of the main window. See After the Windows are Built in this tutorial. While I'm at it, I make the exit choice from the menubar (under file) and the exit button work, using gtk_main_quit(). An example follows:

void on_exit1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
   gtk_main_quit();
}

Yes, that part is that easy.

 
 
Making the Buttons Work

Now, all the 'cancel' and 'ok' buttons from all the popup dialog windows and message windows need to 'destroy' their parents when clicked. Basically this removes the window (don't use destroy on a main window button). To destroy their parents, you find the parent using gtk_widget_get_toplevel(), then use gtk_widget_destroy(). I'm aware that the ok buttons, at least, are going to need quite a bit more code. But in the end, clicking on 'ok' destroys the dialog. Here's an example:

void on_defaults_dialog_ok_button_clicked(GtkButton *button, gpointer user_data)
{
  gtk_widget_destroy(gtk_widget_get_toplevel(GTK_WIDGET(button)));
}

 
 
Creating the Dialog and Popups

There are places where new windows need to be created. For example, when the 'defaults' selection from the settings menu is activated. To create a window, set a GtkWidget *variable, and then use variable = create_widgetname(). The window will be destroyed when its 'ok', or 'cancel' button is clicked, so there is no need to worry about that when creating it. Once the widget is created, you need to show it with gtk_widget_show().

void on_defaults_activate(GtkMenuItem *menuitem, gpointer user_data)
{
   GtkWidget *defaults;

   defaults = create_defaults_dialog();
   gtk_widget_show(defaults);
}

 
 
Grabbing User Input from Text Entries

A lot of this particular program's input or output is with text entrys. To use these, use the gtk_editable_get_chars() function. That will pull whatever the user has entered in the text entry into your variable. I usually set a static flag when one of the entries is changed. Then when the ok button of the dialog window is clicked, I get the text entry with the above function. The following is the function which grabs the file_name for geocenter. I grab the file name when the user clicks the ok button in the load_file_dialog box. Notice how I am also creating error/warning boxes at certain points, if certain conditions aren't reached. While not strictly necessary, these make the interface MUCH more user-friendly. Here's the function:

void on_load_file_dialog_ok_button_clicked(GtkButton *button, gpointer user_data)
{
   GtkWidget *no_such_file_warning = NULL;
   GtkWidget *load_file_dialog_text_entry = NULL;
   GtkWidget *file_name_entered_wrong_error_box = NULL;
   gchar *file_name = NULL;
   gint result = 0;

   if (!FileEntryChanged) {
     file_name_entered_wrong_error_box = create_file_name_entered_wrong_error_box();
     gtk_widget_show(file_name_entered_wrong_error_box);
    FileLoaded = FALSE;
     return;
   }

   load_file_dialog_text_entry = lookup_widget(GTK_WIDGET(button), "load_file_dialog_text_entry");
   file_name = gtk_editable_get_chars(GTK_EDITABLE(load_file_dialog_text_entry), 0, -1);

   /* check to see the file_name is valid */
   if (file_name && isalnum(file_name[0])) {
     result = check_for_file(file_name);
     if (!result) {
       g_free(file_name);
       /* let em know the file doesn't exist */
       no_such_file_warning = create_no_such_file_message_box();
       gtk_widget_show(no_such_file_warning);
       FileLoaded = FALSE;
       return;
     }
     /* load the file and get out of here */
     load_file(file_name);
     FileEntryChanged = FALSE;
     FileLoaded = TRUE;
     g_free(file_name); /* free it, we don't need it anymore */
     gtk_widget_destroy(gtk_widget_get_toplevel(GTK_WIDGET(button)));
     return;
   }
}

The functions check_for_file(), and load_file() are part of the geocenter backend in geocenter.c. All the rest are standard glib, C, or Gtk+ functions. I discovered that I need another warning window about improper entry of the file name. Also I should set the message windows to be modal (ie the user has to click ok before she can do anything else with the program) to avoid clashes. Most windows shouldn't be modal, and the coder should code around any clashes, but I feel warning and error windows need attention. Back in glade, I make the warning boxes modal, and add the new warning/error message box, which looks like the following:

Geo Center Image 5
 

As I mentioned before, it's much easier to use glade while coding, instead of trying to design the whole interface before putting code to file.

 
 
Making User Selections Show up in Other Windows

I want to show the values the user picks in the defaults_dialog in the entry fields on geocenter_main. Because these are two different windows, and defaults_dialog is not in the 'tree' of geocenter_main, I can't use lookup_widget(), or gtk_widget_get_toplevel() to do it. Instead I have to declare a static key, and use key value pairs with the gtk widget's ability to take coder defined object data. Here's how I set it up for defaults_dialog (linking when the dialog window is created). Notice that the Key is a filewide static variable. Also notice that I set up a callback for the optionmenu, so I can tell if it's changed by the user. Glade doesn't support this directly yet, but the Glade FAQ shows how to do it.

static const gchar *GeoCenterMainKey = "GeoCenterMainKey";


void on_defaults_activate(GtkMenuItem *menuitem, gpointer user_data)
{
   GtkWidget *defaults;
   GtkWidget *geocenter_main;
   GtkWidget *option_menu;

   defaults = create_defaults_dialog();
   geocenter_main = lookup_widget(GTK_WIDGET(menuitem), "geocenter_main");
   gtk_object_set_data(GTK_OBJECT(defaults), GeoCenterMainKey, geocenter_main);
   /* set up the option menu properly */
   option_menu = lookup_widget(defaults, "defaults_centerpoint_optionmenu");
   gtk_signal_connect(GTK_OBJECT(GTK_OPTION_MENU(option_menu)->menu),
     "deactivate", GTK_SIGNAL_FUNC(on_centerpoint_option_selected), NULL);
   gtk_widget_show(defaults);
}

 

Now I want to use the object data I set up in the function above to refer back to the main window when the user clicks the ok, or the apply buttons on the defaults_dialog window. And oops, I notice I need another error/warning box, in case the radius isn't entered properly. Instead I decide to use the default if the radius is entered improperly although this isn't user intuitive and really needs to change before final release.

void on_defaults_dialog_ok_button_clicked(GtkButton *button, gpointer user_data)
{
   GtkWidget *radius_entry = NULL;
   GtkWidget *buttonparent = NULL;
  GtkWidget *geocenter_main = NULL;
   GtkWidget *main_radius_entry = NULL;
   GtkWidget *main_centerpoint_entry = NULL;
   gchar *new_centerpoint = NULL;

   /* has the radius changed? if so grab whatever is in there */
   if (RadiusChanged) {
     radius_entry = lookup_widget(GTK_WIDGET(button), "defaults_radius_combo_entry");
     Radius = gtk_editable_get_chars(GTK_EDITABLE(radius_entry), 0, -1);
     if (!Radius || !isdigit(Radius[0])) {
       Radius = g_strdup("100");
     }
     RadiusChanged = FALSE;
   }
   /* find the main window so the changes are displayed there */
   buttonparent = gtk_widget_get_toplevel(GTK_WIDGET(button));
   geocenter_main = gtk_object_get_data(GTK_OBJECT(buttonparent), GeoCenterMainKey);

   /* find the appropriate entries in the main window, and shove the new contents into them */
   main_radius_entry = lookup_widget(GTK_WIDGET(geocenter_main), "radius_entry");
   main_centerpoint_entry = lookup_widget(GTK_WIDGET(geocenter_main), "centerpoint_entry");
   gtk_entry_set_text(GTK_ENTRY(main_radius_entry), Radius);
   /* centerpoint is a bit tricker */
   switch (CenterPoint) {
     case CENTER_SILVERDALE:
       new_centerpoint = g_strdup("Silverdale");
       break;
     case CENTER_BREMERTON:
       new_centerpoint = g_strdup("Bremerton");
       break;
     case CENTER_KINGSTON:
       new_centerpoint = g_strdup("Kingston");
       break;
     case CENTER_SEATTLE:
       new_centerpoint = g_strdup("Seattle");
       break;
     case CENTER_PORT_ORCHARD:
       new_centerpoint = g_strdup("Port Orchard");
       break;
     case CENTER_BAINBRIDGE_ISLAND:
       new_centerpoint = g_strdup("Bainbridge Island");
       break;
     default:
       new_centerpoint = g_strdup("Silverdale");
       break;
   }
   gtk_entry_set_text(GTK_ENTRY(main_centerpoint_entry), new_centerpoint);
   g_free(new_centerpoint); /*free it, don't need it anymore */

   gtk_widget_destroy(gtk_widget_get_toplevel(GTK_WIDGET(button)));
}

 

The apply button handler for the defaults_dialog has the same code, without the call to gtk_widget_destroy(). Thus the user can see the affects as she applies them. And with that, I think I have covered just about everything in callbacks.c for Geocenter, as well as illustrated some of the main skills and functions needed to manipulate the interface as well as link to backend code (not that Geocenter has much of that). Next I will code the backend functions, but as that doesn't have much to do with Glade or Gtk+ or Gnome, I'll refer you to the actual code itself.

Previous Up Next