// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef APP_GTK_SIGNAL_H_ #define APP_GTK_SIGNAL_H_ #include #include #include #include "base/basictypes.h" // At the time of writing this, there were two common ways of binding our C++ // code to the gobject C system. We either defined a whole bunch of "static // MethodThunk()" which just called nonstatic Method()s on a class (which hurt // readability of the headers and signal connection code) OR we declared // "static Method()" and passed in the current object as the gpointer (and hurt // readability in the implementation by having "context->" before every // variable). // The hopeful result of using these macros is that the code will be more // readable and regular. There shouldn't be a bunch of static Thunks visible in // the headers and the implementations shouldn't be filled with "context->" // de-references. #define CHROMEG_CALLBACK_0(CLASS, RETURN, METHOD, SENDER) \ static RETURN METHOD ## Thunk(SENDER sender, gpointer userdata) { \ return reinterpret_cast(userdata)->METHOD(sender); \ } \ \ virtual RETURN METHOD(SENDER); #define CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, SENDER, ARG1) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, \ gpointer userdata) { \ return reinterpret_cast(userdata)->METHOD(sender, one); \ } \ \ virtual RETURN METHOD(SENDER, ARG1); #define CHROMEG_CALLBACK_2(CLASS, RETURN, METHOD, SENDER, ARG1, ARG2) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, ARG2 two, \ gpointer userdata) { \ return reinterpret_cast(userdata)->METHOD(sender, one, two); \ } \ \ virtual RETURN METHOD(SENDER, ARG1, ARG2); #define CHROMEG_CALLBACK_3(CLASS, RETURN, METHOD, SENDER, ARG1, ARG2, ARG3) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, ARG2 two, \ ARG3 three, gpointer userdata) { \ return reinterpret_cast(userdata)-> \ METHOD(sender, one, two, three); \ } \ \ virtual RETURN METHOD(SENDER, ARG1, ARG2, ARG3); #define CHROMEG_CALLBACK_4(CLASS, RETURN, METHOD, SENDER, ARG1, ARG2, ARG3, \ ARG4) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, ARG2 two, \ ARG3 three, ARG4 four, \ gpointer userdata) { \ return reinterpret_cast(userdata)-> \ METHOD(sender, one, two, three, four); \ } \ \ virtual RETURN METHOD(SENDER, ARG1, ARG2, ARG3, ARG4); #define CHROMEG_CALLBACK_5(CLASS, RETURN, METHOD, SENDER, ARG1, ARG2, ARG3, \ ARG4, ARG5) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, ARG2 two, \ ARG3 three, ARG4 four, ARG5 five, \ gpointer userdata) { \ return reinterpret_cast(userdata)-> \ METHOD(sender, one, two, three, four, five); \ } \ \ virtual RETURN METHOD(SENDER, ARG1, ARG2, ARG3, ARG4, ARG5); #define CHROMEG_CALLBACK_6(CLASS, RETURN, METHOD, SENDER, ARG1, ARG2, ARG3, \ ARG4, ARG5, ARG6) \ static RETURN METHOD ## Thunk(SENDER sender, ARG1 one, ARG2 two, \ ARG3 three, ARG4 four, ARG5 five, \ ARG6 six, gpointer userdata) { \ return reinterpret_cast(userdata)-> \ METHOD(sender, one, two, three, four, five, six); \ } \ \ virtual RETURN METHOD(SENDER, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6); // These macros handle the common case where the sender object will be a // GtkWidget*. #define CHROMEGTK_CALLBACK_0(CLASS, RETURN, METHOD) \ CHROMEG_CALLBACK_0(CLASS, RETURN, METHOD, GtkWidget*); #define CHROMEGTK_CALLBACK_1(CLASS, RETURN, METHOD, ARG1) \ CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, GtkWidget*, ARG1); #define CHROMEGTK_CALLBACK_2(CLASS, RETURN, METHOD, ARG1, ARG2) \ CHROMEG_CALLBACK_2(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2); #define CHROMEGTK_CALLBACK_3(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3) \ CHROMEG_CALLBACK_3(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3); #define CHROMEGTK_CALLBACK_4(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4) \ CHROMEG_CALLBACK_4(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, ARG4); #define CHROMEGTK_CALLBACK_5(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4, \ ARG5) \ CHROMEG_CALLBACK_5(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, \ ARG4, ARG5); #define CHROMEGTK_CALLBACK_6(CLASS, RETURN, METHOD, ARG1, ARG2, ARG3, ARG4, \ ARG5, ARG6) \ CHROMEG_CALLBACK_6(CLASS, RETURN, METHOD, GtkWidget*, ARG1, ARG2, ARG3, \ ARG4, ARG5, ARG6); // A class that ensures that callbacks don't run on stale owner objects. Similar // in spirit to NotificationRegistrar. Use as follows: // // class ChromeObject { // public: // ChromeObject() { // ... // // signals_.Connect(widget, "event", CallbackThunk, this); // } // // ... // // private: // GtkSignalRegistrar signals_; // }; // // When |signals_| goes down, it will disconnect the handlers connected via // Connect. class GtkSignalRegistrar { public: GtkSignalRegistrar(); ~GtkSignalRegistrar(); // Connect before the default handler. Returns the handler id. glong Connect(gpointer instance, const gchar* detailed_signal, GCallback signal_handler, gpointer data); // Connect after the default handler. Returns the handler id. glong ConnectAfter(gpointer instance, const gchar* detailed_signal, GCallback signal_handler, gpointer data); private: static void WeakNotifyThunk(gpointer data, GObject* where_the_object_was) { reinterpret_cast(data)->WeakNotify( where_the_object_was); } void WeakNotify(GObject* where_the_object_was); glong ConnectInternal(gpointer instance, const gchar* detailed_signal, GCallback signal_handler, gpointer data, bool after); typedef std::vector HandlerList; typedef std::map HandlerMap; HandlerMap handler_lists_; DISALLOW_COPY_AND_ASSIGN(GtkSignalRegistrar); }; #endif // APP_GTK_SIGNAL_H_