
/*
 * Build with:
 * gcc testmenu.c -o testmenu -Wall `pkg-config gtk+-3.0 --cflags --libs`
 */

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

#define N_ENTRIES 20
#define UPDATE_INTERVAL 2 /* Higher values such as 100 won't trigger the race condition */

typedef struct _MyMenu MyMenu;

struct _MyMenu {
  GtkWidget *menu;
  GtkWidget *menuitem[N_ENTRIES];
  gboolean   show_it[N_ENTRIES];

  guint update_context_menus_timeout_id;
};

static void
on_menu_item_activate (GtkMenuItem *menuitem,
                       gpointer     data)
{
    gboolean *show_it = (gboolean *) data;

    *show_it = !*show_it;
}

static void
update_my_menu_entries (MyMenu *my_menu)
{
    gint    i;

    for (i = 0; i < N_ENTRIES; i++)
      if (my_menu->show_it[i])
        gtk_widget_show (my_menu->menuitem[i]);
      else
        gtk_widget_hide (my_menu->menuitem[i]);
}

static gboolean
update_context_menus_timeout_callback (gpointer data)
{
    MyMenu *my_menu = (MyMenu *) data;

    my_menu->update_context_menus_timeout_id = 0;
    update_my_menu_entries (my_menu);

    return FALSE;
}

static void
schedule_update_context_menus (MyMenu *my_menu)
{
    if (my_menu->update_context_menus_timeout_id == 0)
      my_menu->update_context_menus_timeout_id
            = g_timeout_add (UPDATE_INTERVAL, update_context_menus_timeout_callback, my_menu);
}


static gboolean
on_button_press (GtkWidget *widget,
                 GdkEvent  *event,
                 gpointer   data)
{
    MyMenu *my_menu = (MyMenu *) data;

    schedule_update_context_menus (my_menu);
    gtk_menu_popup_at_pointer (GTK_MENU (my_menu->menu), event);
    return TRUE;
}

int
main (int argc, char *argv[])
{
    GtkWidget *window;
    GtkWidget *eventbox;
    MyMenu    *my_menu;
    gchar      buffer[32];
    gint       i;

    gtk_init (&argc, &argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    g_signal_connect (window, "delete-event", G_CALLBACK (gtk_main_quit), NULL);

    my_menu = g_new0 (MyMenu, 1);
    my_menu->menu = gtk_menu_new ();

    for (i = 0; i < N_ENTRIES; i++)
      {
        g_snprintf (buffer, sizeof(buffer), "Menu item #%i", i + 1);

        my_menu->menuitem[i] = gtk_menu_item_new_with_label (buffer);
        my_menu->show_it[i] = TRUE;

        g_signal_connect (my_menu->menuitem[i], "activate", 
                          G_CALLBACK (on_menu_item_activate), &my_menu->show_it[i]);
        gtk_menu_shell_append (GTK_MENU_SHELL (my_menu->menu), my_menu->menuitem[i]);
        gtk_widget_show (GTK_WIDGET (my_menu->menuitem[i]));
      }

    eventbox = gtk_event_box_new();
    gtk_widget_set_size_request (GTK_WIDGET (eventbox), 400, 300);
    gtk_container_add (GTK_CONTAINER (window), eventbox);
    gtk_widget_show (eventbox);
    g_signal_connect (eventbox, "button-press-event",
                      G_CALLBACK (on_button_press), my_menu);

    gtk_widget_show_all (GTK_WIDGET (window));

    gtk_main ();

    g_free (my_menu);

    return 0;
}
