
#include <gtk/gtk.h>
#include <strings.h>

/* Obligatory basic callback */
static void print_hello( GtkWidget *w,
                         gpointer   data )
{
  g_message ("Hello, World!\n");
}

/* For the check button */
static void print_toggle(gpointer   callback_data,
                         guint      callback_action,
                         GtkWidget *menu_item)
{
   g_message ("Check button state - %d\n",
              GTK_CHECK_MENU_ITEM(menu_item)->active);
}

/* For the radio buttons */
static void print_selected(gpointer   callback_data,
                           guint      callback_action,
                           GtkWidget *menu_item)
{
   if(GTK_CHECK_MENU_ITEM(menu_item)->active)
     g_message("Radio button %d selected\n", callback_action);
}

/* Our menu, an array of GtkItemFactoryEntry structures that defines each menu item */
static GtkItemFactoryEntry menu_items[] = {
  { "/_File",         NULL,         NULL,           0, "<Branch>" },
  { "/File/_New",     "<control>N", print_hello,    0, "<Item>" },
  { "/File/_Open",    "<control>O", print_hello,    0, "<Item>" },
  { "/File/_Save",    "<control>S", print_hello,    0, "<Item>" },
  { "/File/Save _As", NULL,         NULL,           0, "<Item>" },
  { "/File/sep1",     NULL,         NULL,           0, "<Separator>" },
  { "/File/Quit",     "<control>Q", gtk_main_quit,  0, "<Item>" },
  { "/_Options",      NULL,         NULL,           0, "<Branch>" },
  { "/Options/tear",  NULL,         NULL,           0, "<Tearoff>" },
  { "/Options/Check", NULL,         print_toggle,   1, "<CheckItem>" },
  { "/Options/sep",   NULL,         NULL,           0, "<Separator>" },
  { "/Options/Rad1",  NULL,         print_selected, 1, "<RadioItem>" },
  { "/Options/Rad2",  NULL,         print_selected, 2, "/Options/Rad1" },
  { "/Options/Rad3",  NULL,         print_selected, 3, "/Options/Rad1" },
  { "/_Help",         NULL,         NULL,           0, "<LastBranch>" },
  { "/_Help/About",   NULL,         NULL,           0, "<Item>" },
};

static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);

/* Returns a menubar widget made from the above menu */
GtkWidget *get_menubar_menu( GtkWidget  *window)
{
  GtkItemFactory *item_factory;
  GtkAccelGroup *accel_group;

  /* Make an accelerator group (shortcut keys) */
  accel_group = gtk_accel_group_new ();

  /* Make an ItemFactory (that makes a menubar) */
  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
                                       accel_group);

  /* This function generates the menu items. Pass the item factory,
     the number of items in the array, the array itself, and any
     callback data for the the menu items. */
  gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);

  /* Attach the new accelerator group to the window. */
  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);

  /* Finally, return the actual menu bar created by the item factory. */
  return gtk_item_factory_get_widget (item_factory, "<main>");
}

/* Popup the menu when the popup button is pressed */
static gint popup_cb(GtkWidget *widget, GdkEvent *event, GtkWidget *menu)
{
   GdkEventButton *bevent = (GdkEventButton *)event;
  
   /* Only take button presses */
   if(event->type != GDK_BUTTON_PRESS)
     return FALSE;
  
   /* Show the menu */
   gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
                  NULL, NULL, bevent->button, bevent->time);
  
   return TRUE;
}

/* Same as with get_menubar_menu() but just return a button with a signal to
   call a popup menu */
GtkWidget *get_popup_menu(void)
{
   GtkItemFactory *item_factory;
   GtkWidget *button, *menu;
  
   /* Same as before but don't bother with the accelerators */
   item_factory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
                                        NULL);
   gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
   menu = gtk_item_factory_get_widget(item_factory, "<main>");
  
   /* Make a button to activate the popup menu */
   button = gtk_button_new_with_label("Popup");
   /* Make the menu popup when clicked */
   g_signal_connect(G_OBJECT(button),
                    "event",
                    G_CALLBACK(popup_cb),
                    (gpointer) menu);

   return button;
}

/* Same again but return an option menu */
GtkWidget *get_option_menu(void)
{
   GtkItemFactory *item_factory;
   GtkWidget *option_menu;
  
   /* Same again, not bothering with the accelerators */
   item_factory = gtk_item_factory_new (GTK_TYPE_OPTION_MENU, "<main>",
                                        NULL);
   gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL);
   option_menu = gtk_item_factory_get_widget(item_factory, "<main>");

   return option_menu;
}

/* You have to start somewhere */
int main( int argc,
          char *argv[] )
{
  GtkWidget *window;
  GtkWidget *main_vbox;
  GtkWidget *menubar, *option_menu, *popup_button;
 
  /* Initialize GTK */
  gtk_init (&argc, &argv);
 
  /* Make a window */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  g_signal_connect (G_OBJECT (window), "destroy",
                    G_CALLBACK (gtk_main_quit),
                    NULL);
  gtk_window_set_title (GTK_WINDOW(window), "Item Factory");
  gtk_widget_set_size_request (GTK_WIDGET(window), 300, 200);
 
  /* Make a vbox to put the three menus in */
  main_vbox = gtk_vbox_new (FALSE, 1);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 1);
  gtk_container_add (GTK_CONTAINER (window), main_vbox);
 
  /* Get the three types of menu */
  /* Note: all three menus are separately created, so they are not the
     same menu */
  menubar = get_menubar_menu (window);
  popup_button = get_popup_menu();
  option_menu = get_option_menu();
  
  /* Pack it all together */
  gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (main_vbox), popup_button, FALSE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (main_vbox), option_menu, FALSE, TRUE, 0);

  /* Show the widgets */
  gtk_widget_show_all (window);
  
  /* Finished! */
  gtk_main ();
 
  return(0);
}
