#include <stdio.h>

#include <glibmm.h>
#include <gtkmm.h>

#include <string>
#include <map>

class MyWin : public Gtk::Window
{
public: // just a testcase, no need to panic

    Gtk::VBox * top_vbox;
    Gtk::Widget * menubar;
    Glib::RefPtr <Gtk::ActionGroup> actiongp;
    Glib::RefPtr <Gtk::UIManager> uimgr;

    void create_action (const char * const name,
                        const char * const menustr,
                        void (MyWin::* actionfunc)
                        (const char * name,
                         Glib::RefPtr <Gtk::ToggleAction> tact),
                        const char * const tooltip,
                        const Gtk::StockID stock_id)
    {
        std::string namestr = name;
        Glib::RefPtr <Gtk::ToggleAction> action
            = Gtk::ToggleAction::create (namestr, stock_id, menustr, tooltip);
        actiongp->add (action, sigc::bind <const char *,
                       Glib::RefPtr <Gtk::ToggleAction> >
                       (sigc::mem_fun (*this, actionfunc), name, action));
    }

    void on_action_sample (const char * name, 
                           Glib::RefPtr <Gtk::ToggleAction> tact)
    {
    }

    bool on_delete_event (GdkEventAny *)
    {
        Gtk::Main::quit();
        return false;
    }

    MyWin ()
        : top_vbox (0),
          menubar (0),
          actiongp (Gtk::ActionGroup::create()),
          uimgr (Gtk::UIManager::create())
    {
        actiongp->add (Gtk::Action::create ("FileMenu", "File"));

        create_action ("SampleAction", "Do something", &MyWin::on_action_sample,
                       "Whatever", Gtk::StockID (0));

        uimgr->insert_action_group (actiongp);

        Glib::ustring ui_info = 
            "<ui>"
            "  <menubar name='MenuBar'>"
            "    <menu action='FileMenu'>"
            "      <menuitem action='SampleAction'/>"
            "    </menu>"
            "  </menubar>"
            "</ui>";

        try
        {
            uimgr->add_ui_from_string (ui_info);
        }
        catch (const Glib::Error & ex)
        {
            fprintf (stderr, "window: building menus failed: %s",
                     ex.what().c_str());
            return;
        }

        Glib::RefPtr <Gtk::Builder> builder = Gtk::Builder::create();

        const char * name = "top_vbox";

        try
        {
            builder->add_from_string ("<?xml version=\"1.0\"?>\
<interface>\
  <requires lib=\"gtk+\" version=\"2.16\"/>\
  <!-- interface-naming-policy toplevel-contextual -->\
  <object class=\"GtkVBox\" id=\"top_vbox\">\
    <property name=\"visible\">True</property>\
    <property name=\"orientation\">vertical</property>\
  </object>\
</interface>",
                                      name);
        }
        catch (const Glib::FileError & ex)
        {
            fprintf (stderr, "FileError creating %s: %s",
                     name, ex.what().c_str());
            return;
        }
        catch (const Gtk::BuilderError & ex)
        {
            fprintf (stderr, "BuilderError creating %s: %s",
                     name, ex.what().c_str());
            return;
        }

        top_vbox = 0;
        builder->get_widget ("top_vbox", top_vbox);
        if (!top_vbox)
            fprintf (stderr, "Failed to find %s", name);

        add (*top_vbox);

        // This is critical: the menubar (with the actions with signals with
        // bound ToggleAction RefPtr's) must be added to a widget that came
        // from Gtk::Builder, or the problem does not occur.  FWTF?

        menubar = uimgr->get_widget ("/MenuBar");
        if (menubar)
        {
            top_vbox->pack_start (*menubar, Gtk::PACK_SHRINK);
            top_vbox->reorder_child (*menubar, 0);
        }

        set_default_size (1024, 768);
        show_all();
    }

    ~MyWin ()
    {

// Without the line inside #ifdef AVOID_PROBLEM below, I see crashes in the
// real-world application from which this testcase was derived, and valgrind
// (3.6.0 on Ubuntu 10.10) says this about this testcase:

// ==27282== Invalid read of size 8
// ==27282==    at 0x68DD050: sigc::internal::slot_rep::disconnect() (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x68DD0AE: sigc::internal::slot_rep::notify(void*) (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x68DCB9E: sigc::internal::trackable_callback_list::~trackable_callback_list() (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x68DCC80: sigc::trackable::notify_callbacks() (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x405FFF: MyWin::~MyWin() (problem-110212.cpp:172)
// ==27282==    by 0x405D91: main (problem-110212.cpp:185)
// ==27282==  Address 0xce1fe88 is 40 bytes inside a block of size 112 free'd
// ==27282==    at 0x4C27A83: operator delete(void*) (vg_replace_malloc.c:387)
// ==27282==    by 0x649CD09: Glib::SignalProxyConnectionNode::destroy_notify_handler(void*, _GClosure*) (in /usr/lib/libglibmm-2.4.so.1.3.0)
// ==27282==    by 0x85005AA: g_closure_unref (in /usr/lib/libgobject-2.0.so.0.2600.0)
// ==27282==    by 0x85156CC: g_signal_handlers_destroy (in /usr/lib/libgobject-2.0.so.0.2600.0)
// ==27282==    by 0x850282C: ??? (in /usr/lib/libgobject-2.0.so.0.2600.0)
// ==27282==    by 0x8502999: g_object_unref (in /usr/lib/libgobject-2.0.so.0.2600.0)
// ==27282==    by 0x406255: sigc::internal::typed_slot_rep<sigc::bind_functor<-1, sigc::bound_mem_functor2<void, MyWin, char const*, Glib::RefPtr<Gtk::ToggleAction> >, char const*, Glib::RefPtr<Gtk::ToggleAction>, sigc::nil, sigc::nil, sigc::nil, sigc::nil, sigc::nil> >::destroy(void*) (refptr.h:184)
// ==27282==    by 0x68DD0A6: sigc::internal::slot_rep::notify(void*) (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x68DCB9E: sigc::internal::trackable_callback_list::~trackable_callback_list() (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x68DCC80: sigc::trackable::notify_callbacks() (in /usr/lib/libsigc-2.0.so.0.0.0)
// ==27282==    by 0x405FFF: MyWin::~MyWin() (problem-110212.cpp:172)
// ==27282==    by 0x405D91: main (problem-110212.cpp:185)

#ifdef AVOID_PROBLEM
        top_vbox->remove (*menubar);
        // delete menubar; // Do I want this?  Bad things happen if I do it.
#endif

#if 0 // none of this helps at all
        uimgr->remove_action_group (actiongp);
        uimgr.reset();
        for (ActionMap::iterator it = actions.begin();
             it != actions.end();
             ++it)
            actiongp->remove (it->second);
        actiongp.reset(); // effectively, "delete actiongp"; redundant
#endif
    }
};

int main (int argc, char * argv [])
{
    Glib::thread_init();
    gdk_threads_init();
    gdk_threads_enter();
    new Gtk::Main (argc, argv);
    MyWin * win = new MyWin;
    fprintf (stderr, "about to run\n");
    Gtk::Main::run (*win);
    fprintf (stderr, "about to delete win\n");
    delete win;
    gdk_threads_leave();
    return 0;
}
