Back to index

lightning-sunbird  0.9+nobinonly
gtkmozarea.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Owen Taylor <otaylor@redhat.com> and Christopher Blizzard 
00020  * <blizzard@redhat.com>.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "gtkmozarea.h"
00041 #include <gtk/gtk.h>
00042 #include <gdk/gdkx.h>
00043 
00044 static void gtk_mozarea_class_init (GtkMozAreaClass *klass);
00045 static void gtk_mozarea_init       (GtkMozArea      *mozarea);
00046 static void gtk_mozarea_realize    (GtkWidget       *widget);
00047 static void gtk_mozarea_unrealize  (GtkWidget       *widget);
00048 static void gtk_mozarea_size_allocate (GtkWidget    *widget,
00049                                        GtkAllocation *allocation);
00050 static void gtk_mozarea_destroy    (GtkObject       *object);
00051 
00052 static void
00053 attach_toplevel_listener(GtkMozArea *mozarea);
00054 
00055 static void
00056 remove_toplevel_listener(GtkMozArea *mozarea);
00057 
00058 static Window
00059 get_real_toplevel(Window aWindow);
00060 
00061 static GdkWindow *
00062 get_real_gdk_toplevel(GtkMozArea *mozarea);
00063 
00064 static GdkFilterReturn
00065 toplevel_window_filter(GdkXEvent   *aGdkXEvent,
00066                        GdkEvent    *aEvent,
00067                        gpointer     data);
00068 
00069 static GdkFilterReturn
00070 widget_window_filter(GdkXEvent     *aGdkXEvent,
00071                      GdkEvent      *aEvent,
00072                      gpointer       data);
00073 
00074 GtkWidgetClass *parent_class = NULL;
00075 
00076 enum {
00077   TOPLEVEL_FOCUS_IN,
00078   TOPLEVEL_FOCUS_OUT,
00079   TOPLEVEL_CONFIGURE,
00080   LAST_SIGNAL
00081 };
00082 
00083 static guint mozarea_signals[LAST_SIGNAL] = { 0 };
00084 
00085 GtkType
00086 gtk_mozarea_get_type (void)
00087 {
00088   static GtkType mozarea_type = 0;
00089   
00090   if (!mozarea_type)
00091     {
00092       static const GtkTypeInfo mozarea_info =
00093       {
00094         "GtkMozArea",
00095         sizeof (GtkMozArea),
00096         sizeof (GtkMozAreaClass),
00097         (GtkClassInitFunc) gtk_mozarea_class_init,
00098         (GtkObjectInitFunc) gtk_mozarea_init,
00099         /* reserved_1 */ NULL,
00100         /* reserved_2 */ NULL,
00101         (GtkClassInitFunc) NULL
00102       };
00103       
00104       mozarea_type = gtk_type_unique (GTK_TYPE_WIDGET, &mozarea_info);
00105     }
00106 
00107   return mozarea_type;
00108 }
00109 
00110 static void
00111 gtk_mozarea_class_init (GtkMozAreaClass *klass)
00112 {
00113   GtkWidgetClass *widget_class;
00114   GtkObjectClass *object_class;
00115 
00116   widget_class = GTK_WIDGET_CLASS (klass);
00117   object_class = GTK_OBJECT_CLASS (klass);
00118 
00119   widget_class->realize = gtk_mozarea_realize;
00120   widget_class->unrealize = gtk_mozarea_unrealize;
00121   widget_class->size_allocate = gtk_mozarea_size_allocate;
00122 
00123   object_class->destroy = gtk_mozarea_destroy;
00124 
00125   parent_class = gtk_type_class(gtk_widget_get_type());
00126 
00127   /* set up our signals */
00128 
00129   mozarea_signals[TOPLEVEL_FOCUS_IN] =
00130     gtk_signal_new("toplevel_focus_in",
00131                    GTK_RUN_FIRST,
00132                    object_class->type,
00133                    GTK_SIGNAL_OFFSET(GtkMozAreaClass, toplevel_focus_in),
00134                    gtk_marshal_NONE__NONE,
00135                    GTK_TYPE_NONE, 0);
00136   mozarea_signals[TOPLEVEL_FOCUS_OUT] =
00137     gtk_signal_new("toplevel_focus_out",
00138                    GTK_RUN_FIRST,
00139                    object_class->type,
00140                    GTK_SIGNAL_OFFSET(GtkMozAreaClass, toplevel_focus_out),
00141                    gtk_marshal_NONE__NONE,
00142                    GTK_TYPE_NONE, 0);
00143   mozarea_signals[TOPLEVEL_CONFIGURE] =
00144     gtk_signal_new("toplevel_configure",
00145                    GTK_RUN_FIRST,
00146                    object_class->type,
00147                    GTK_SIGNAL_OFFSET(GtkMozAreaClass, toplevel_configure),
00148                    gtk_marshal_NONE__NONE,
00149                    GTK_TYPE_NONE, 0);
00150   gtk_object_class_add_signals(object_class, mozarea_signals, LAST_SIGNAL);
00151 
00152 }
00153 
00154 static void
00155 gtk_mozarea_init (GtkMozArea *mozarea)
00156 {
00157   mozarea->superwin = NULL;
00158   mozarea->toplevel_focus = FALSE;
00159   mozarea->toplevel_window = NULL;
00160   gtk_widget_set_name(GTK_WIDGET(mozarea), "gtkmozarea");
00161 }
00162 
00163 static void 
00164 gtk_mozarea_realize (GtkWidget *widget)
00165 {
00166   GtkMozArea *mozarea;
00167   
00168   g_return_if_fail (GTK_IS_MOZAREA (widget));
00169 
00170   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
00171 
00172   mozarea = GTK_MOZAREA (widget);
00173 
00174   mozarea->superwin = gdk_superwin_new (gtk_widget_get_parent_window (widget),
00175                                         widget->allocation.x,
00176                                         widget->allocation.y,
00177                                         widget->allocation.width,
00178                                         widget->allocation.height);
00179   gdk_window_set_user_data (mozarea->superwin->shell_window, mozarea);
00180   widget->window = mozarea->superwin->shell_window;
00181   widget->style = gtk_style_attach (widget->style, widget->window);
00182   /* make sure that we add a reference to this window.
00183      if we don't then it will be destroyed by both the superwin
00184      destroy method and the widget class destructor */
00185   gdk_window_ref(widget->window);
00186 
00187   /* attach the toplevel X listener */
00188   attach_toplevel_listener(mozarea);
00189 
00190   /* Attach a filter so that we can get reparent notifications so we
00191      can reset our toplevel window.  This will automatically be
00192      removed when our window is destroyed. */
00193   gdk_window_add_filter(widget->window, widget_window_filter, mozarea);
00194 }
00195 
00196 static void
00197 gtk_mozarea_unrealize(GtkWidget *widget)
00198 {
00199   GtkMozArea *mozarea;
00200   
00201   g_return_if_fail(GTK_IS_MOZAREA(widget));
00202 
00203   GTK_WIDGET_UNSET_FLAGS(widget, GTK_REALIZED);
00204 
00205   mozarea = GTK_MOZAREA(widget);
00206   
00207   if (mozarea->superwin) {
00208     gtk_object_unref(GTK_OBJECT(mozarea->superwin));
00209     mozarea->superwin = NULL;
00210   }
00211   GTK_WIDGET_CLASS(parent_class)->unrealize(widget);
00212 }
00213 
00214 static void 
00215 gtk_mozarea_size_allocate (GtkWidget    *widget,
00216                         GtkAllocation *allocation)
00217 {
00218   GtkMozArea *mozarea;
00219   
00220   g_return_if_fail (GTK_IS_MOZAREA (widget));
00221 
00222   mozarea = GTK_MOZAREA (widget);
00223 
00224   if (GTK_WIDGET_REALIZED (widget))
00225     {
00226       gdk_window_move (mozarea->superwin->shell_window,
00227                      allocation->x, allocation->y);
00228       gdk_superwin_resize (mozarea->superwin,
00229                         allocation->width, allocation->height);
00230     }
00231 }
00232 
00233 static void
00234 gtk_mozarea_destroy(GtkObject *object)
00235 {
00236   GtkMozArea *mozarea;
00237 
00238   g_return_if_fail(GTK_IS_MOZAREA(object));
00239   
00240   mozarea = GTK_MOZAREA(object);
00241 
00242   /* remove the listener from the toplevel */
00243   remove_toplevel_listener(mozarea);
00244 
00245   GTK_OBJECT_CLASS(parent_class)->destroy(object);
00246 }
00247 
00248 GtkWidget*
00249 gtk_mozarea_new (GdkWindow *parent_window)
00250 {
00251   return GTK_WIDGET (gtk_type_new (GTK_TYPE_MOZAREA));
00252 }
00253 
00254 gboolean
00255 gtk_mozarea_get_toplevel_focus(GtkMozArea *area)
00256 {
00257   g_return_val_if_fail(GTK_IS_MOZAREA(area), FALSE);
00258   
00259   return area->toplevel_focus;
00260 }
00261                                
00262 /* this function will attach a listener to this widget's real toplevel
00263    window */
00264 static void
00265 attach_toplevel_listener(GtkMozArea *mozarea)
00266 {
00267   /* get the real gdk toplevel window */
00268   GdkWindow *gdk_window = get_real_gdk_toplevel(mozarea);
00269 
00270   /* attach our passive filter.  when this widget is destroyed it will
00271      be removed in our destroy method. */
00272   gdk_window_add_filter(gdk_window, toplevel_window_filter, mozarea);
00273 
00274   /* save it so that we can remove the filter later */
00275   mozarea->toplevel_window = gdk_window;
00276 }
00277 
00278 /* this function will remove a toplevel listener for a widget */
00279 static void
00280 remove_toplevel_listener(GtkMozArea *mozarea)
00281 {
00282   gdk_window_remove_filter(mozarea->toplevel_window, toplevel_window_filter,
00283                            mozarea);
00284 }
00285 
00286 /* this function will try to find the real toplevel for a gdk window. */
00287 static Window
00288 get_real_toplevel(Window aWindow)
00289 {
00290   Window current = aWindow;
00291   Atom   atom;
00292 
00293   /* get the atom for the WM_STATE variable that you get on WM
00294      managed windows. */
00295 
00296   atom = XInternAtom(GDK_DISPLAY(), "WM_STATE", FALSE);
00297 
00298   while (current) {
00299     Atom type = None;
00300     int format;
00301     unsigned long nitems, after;
00302     unsigned char *data;
00303 
00304     Window root_return;
00305     Window parent_return;
00306     Window *children_return = NULL;
00307     unsigned int nchildren_return;
00308 
00309     /* check for the atom on this window */
00310     XGetWindowProperty(GDK_DISPLAY(), current, atom,
00311                      0, 0, /* offsets */
00312                      False, /* don't delete */
00313                      AnyPropertyType,
00314                      &type, &format, &nitems, &after, &data);
00315 
00316     /* did we get something? */
00317     if (type != None) {
00318       XFree(data);
00319       data = NULL;
00320       /* ok, this is the toplevel window since it has the property set
00321          on it. */
00322       break;
00323     }
00324     
00325     /* what is the parent? */
00326     XQueryTree(GDK_DISPLAY(), current, &root_return,
00327               &parent_return, &children_return,
00328               &nchildren_return);
00329 
00330     if (children_return)
00331       XFree(children_return);
00332 
00333     /* If the parent window of this window is the root window then
00334      there is no window manager running so the current window is the
00335      toplevel window. */
00336     if (parent_return == root_return)
00337       break;
00338 
00339     current = parent_return;
00340   }
00341 
00342   return current;
00343 }
00344 
00345 static GdkWindow *
00346 get_real_gdk_toplevel(GtkMozArea *mozarea)
00347 {
00348   /* get the native window for this widget */
00349   GtkWidget *widget = GTK_WIDGET(mozarea);
00350   Window window = GDK_WINDOW_XWINDOW(widget->window);
00351   
00352   Window toplevel = get_real_toplevel(window);
00353   
00354   /* check to see if this is an already registered window with the
00355      type system. */
00356   GdkWindow *gdk_window = gdk_window_lookup(toplevel);
00357   
00358   /* This isn't our window?  It is now, fool! */
00359   if (!gdk_window) {
00360     /* import it into the type system */
00361     gdk_window = gdk_window_foreign_new(toplevel);
00362     /* make sure that we are listening for the right events on it. */
00363     gdk_window_set_events(gdk_window, GDK_FOCUS_CHANGE_MASK);
00364   }
00365 
00366   return gdk_window;
00367 }
00368 
00369 static GdkFilterReturn
00370 toplevel_window_filter(GdkXEvent   *aGdkXEvent,
00371                        GdkEvent    *aEvent,
00372                        gpointer     data)
00373 {
00374   XEvent       *xevent = (XEvent *)aGdkXEvent;
00375   GtkMozArea   *mozarea = (GtkMozArea *)data;
00376 
00377   switch (xevent->xany.type) {
00378   case FocusIn:
00379     mozarea->toplevel_focus = TRUE;
00380     gtk_signal_emit(GTK_OBJECT(mozarea), mozarea_signals[TOPLEVEL_FOCUS_IN]);
00381     break;
00382   case FocusOut:
00383     mozarea->toplevel_focus = FALSE;
00384     gtk_signal_emit(GTK_OBJECT(mozarea), mozarea_signals[TOPLEVEL_FOCUS_OUT]);
00385     break;
00386   case ConfigureNotify:
00387     gtk_signal_emit(GTK_OBJECT(mozarea), mozarea_signals[TOPLEVEL_CONFIGURE]);
00388     break;
00389   default:
00390     break;
00391   }
00392 
00393   return GDK_FILTER_CONTINUE;
00394 }
00395 
00396 static GdkFilterReturn
00397 widget_window_filter(GdkXEvent     *aGdkXEvent,
00398                      GdkEvent      *aEvent,
00399                      gpointer       data)
00400 {
00401   XEvent       *xevent = (XEvent *)aGdkXEvent;
00402   GtkMozArea   *mozarea = (GtkMozArea *)data;
00403 
00404   switch(xevent->xany.type) {
00405   case ReparentNotify:
00406     /* remove the old filter on the toplevel and attach to the new one */
00407     remove_toplevel_listener(mozarea);
00408     attach_toplevel_listener(mozarea);
00409     break;
00410   default:
00411     break;
00412   }
00413   return GDK_FILTER_CONTINUE;
00414 }