blob: ffa487b685ca79edf447aea291eb6a64ffde6014 [file] [log] [blame]
// 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 <gtk/gtk.h>
#include <map>
#include <vector>
#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<CLASS*>(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<CLASS*>(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<CLASS*>(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<CLASS*>(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<CLASS*>(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<CLASS*>(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<CLASS*>(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<GtkSignalRegistrar*>(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<glong> HandlerList;
typedef std::map<GObject*, HandlerList> HandlerMap;
HandlerMap handler_lists_;
DISALLOW_COPY_AND_ASSIGN(GtkSignalRegistrar);
};
#endif // APP_GTK_SIGNAL_H_