b=485125 nsIWidget::SetParent(nsnull) for gtk. r=roc

This commit is contained in:
Karl Tomlinson 2009-05-15 14:14:45 +12:00
Родитель b30669d3f7
Коммит a60b90eae7
6 изменённых файлов: 245 добавлений и 58 удалений

Просмотреть файл

@ -375,7 +375,7 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget)
{
MozContainerChild *child;
MozContainer *moz_container;
GList *tmp_list;
GdkWindow* parent_window;
g_return_if_fail (IS_MOZ_CONTAINER(container));
g_return_if_fail (GTK_IS_WIDGET(child_widget));
@ -385,8 +385,34 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget)
child = moz_container_get_child (moz_container, child_widget);
g_return_if_fail (child);
if(child->widget == child_widget) {
gtk_widget_unparent(child_widget);
/* gtk_widget_unparent will remove the parent window (as well as the
* parent widget), but, in Mozilla's window hierarchy, the parent window
* may need to be kept because it may be part of a GdkWindow sub-hierarchy
* that is being moved to another MozContainer.
*
* (In a conventional GtkWidget hierarchy, GdkWindows being reparented
* would have their own GtkWidget and that widget would be the one being
* reparented. In Mozilla's hierarchy, the parent_window needs to be
* retained so that the GdkWindow sub-hierarchy is maintained.)
*/
parent_window = gtk_widget_get_parent_window(child_widget);
if (parent_window)
g_object_ref(parent_window);
gtk_widget_unparent(child_widget);
if (parent_window) {
/* The child_widget will always still exist because g_signal_emit,
* which invokes this function, holds a reference.
*
* If parent_window is the container's root window then it will not be
* the parent_window if the child_widget is placed in another
* container.
*/
if (parent_window != GTK_WIDGET(container)->window)
gtk_widget_set_parent_window(child_widget, parent_window);
g_object_unref(parent_window);
}
moz_container->children = g_list_remove(moz_container->children, child);

Просмотреть файл

@ -45,6 +45,37 @@
extern "C" {
#endif /* __cplusplus */
/*
* MozContainer
*
* This class serves two purposes in the nsIWidget implementation.
*
* - It provides objects to receive signals from GTK for events on native
* windows.
*
* - It provides a container parent for GtkWidgets. The only GtkWidgets
* that need this in Mozilla are the GtkSockets for windowed plugins (Xt
* and XEmbed).
*
* Note that the window hierarchy in Mozilla differs from conventional
* GtkWidget hierarchies.
*
* Mozilla's hierarchy exists through the GdkWindow hierarchy, and all child
* GdkWindows (within a child nsIWidget hierarchy) belong to one MozContainer
* GtkWidget. If the MozContainer is unrealized or its GdkWindows are
* destroyed for some other reason, then the hierarchy no longer exists. (In
* conventional GTK clients, the hierarchy is recorded by the GtkWidgets, and
* so can be re-established after destruction of the GdkWindows.)
*
* One consequence of this is that the MozContainer does not know which of its
* GdkWindows should parent child GtkWidgets. (Conventional GtkContainers
* determine which GdkWindow to assign child GtkWidgets.)
*
* Therefore, when adding a child GtkWidget to a MozContainer,
* gtk_widget_set_parent_window should be called on the child GtkWidget before
* it is realized.
*/
#define MOZ_CONTAINER_TYPE (moz_container_get_type())
#define MOZ_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOZ_CONTAINER_TYPE, MozContainer))
#define MOZ_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOZ_CONTAINER_TYPE, MozContainerClass))

Просмотреть файл

@ -124,24 +124,6 @@ moz_drawingarea_reparent (MozDrawingarea *drawingarea, GdkWindow *aNewParent)
aNewParent, 0, 0);
}
static void
nullify_widget_pointers (gpointer data, GObject *widget)
{
MozDrawingarea *drawingarea = data;
#ifdef DEBUG
gpointer user_data;
/* This function may get called twice before widget is destroyed,
so the user_data may have already been nullified. */
gdk_window_get_user_data(drawingarea->inner_window, &user_data);
if (user_data && widget && user_data != widget)
g_critical("user_data does not match widget");
#endif
gdk_window_set_user_data(drawingarea->inner_window, NULL);
gdk_window_set_user_data(drawingarea->clip_window, NULL);
}
void
moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent,
GtkWidget *widget, GdkVisual *visual)
@ -189,8 +171,6 @@ moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent,
&attributes, attributes_mask);
gdk_window_set_user_data(drawingarea->inner_window, widget);
g_object_weak_ref(G_OBJECT(widget), nullify_widget_pointers, drawingarea);
/* set the default pixmap to None so that you don't end up with the
gtk default which is BlackPixel. */
gdk_window_set_back_pixmap(drawingarea->inner_window, NULL, FALSE);
@ -210,12 +190,6 @@ moz_drawingarea_finalize (GObject *object)
drawingarea = MOZ_DRAWINGAREA(object);
gdk_window_get_user_data(drawingarea->inner_window, &user_data);
if (user_data) {
g_object_weak_unref(user_data, nullify_widget_pointers, drawingarea);
nullify_widget_pointers(drawingarea, NULL);
}
gdk_window_destroy(drawingarea->inner_window);
gdk_window_destroy(drawingarea->clip_window);

Просмотреть файл

@ -65,6 +65,8 @@ typedef struct _MozDrawingareaClass MozDrawingareaClass;
struct _MozDrawingarea
{
GObject parent_instance;
/* AFAIK this clip_window (and thus this whole class) exists solely to
* make gdk_window_scroll() smooth for nsIWidget::Scroll(). */
GdkWindow *clip_window;
GdkWindow *inner_window;
};

Просмотреть файл

@ -177,6 +177,7 @@ static gboolean expose_event_cb (GtkWidget *widget,
GdkEventExpose *event);
static gboolean configure_event_cb (GtkWidget *widget,
GdkEventConfigure *event);
static void container_unrealize_cb (GtkWidget *widget);
static void size_allocate_cb (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean delete_event_cb (GtkWidget *widget,
@ -355,6 +356,8 @@ PRBool gDisableNativeTheme = PR_FALSE;
// created for exposes, even if the display has a different depth
static PRBool gForce24bpp = PR_FALSE;
static GtkWidget *gInvisibleContainer = NULL;
nsWindow::nsWindow()
{
mIsTopLevel = PR_FALSE;
@ -642,6 +645,79 @@ nsWindow::Create(nsNativeWidget aParent,
return rv;
}
static GtkWidget*
EnsureInvisibleContainer()
{
if (!gInvisibleContainer) {
// GtkWidgets need to be anchored to a GtkWindow to be realized (to
// have a window). Using GTK_WINDOW_POPUP rather than
// GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less
// initialization and window manager interaction.
GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
gInvisibleContainer = moz_container_new();
gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer);
gtk_widget_realize(gInvisibleContainer);
}
return gInvisibleContainer;
}
static void
CheckDestroyInvisibleContainer()
{
NS_PRECONDITION(gInvisibleContainer, "oh, no");
if (!gdk_window_peek_children(gInvisibleContainer->window)) {
// No children, so not in use.
// Make sure to destroy the GtkWindow also.
gtk_widget_destroy(gInvisibleContainer->parent);
gInvisibleContainer = NULL;
}
}
// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging
// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of
// the GdkWindow hierarchy. If aNewWidget is NULL, the reference to
// aOldWidget is removed from its GdkWindows, and child GtkWidgets are
// destroyed.
static void
SetWidgetForHierarchy(GdkWindow *aWindow,
GtkWidget *aOldWidget,
GtkWidget *aNewWidget)
{
gpointer data;
gdk_window_get_user_data(aWindow, &data);
if (data != aOldWidget) {
if (!GTK_IS_WIDGET(data))
return;
GtkWidget* widget = static_cast<GtkWidget*>(data);
if (widget->parent != aOldWidget)
return;
// This window belongs to a child widget, which will no longer be a
// child of aOldWidget.
if (aNewWidget) {
gtk_widget_reparent(widget, aNewWidget);
} else {
// aNewWidget == NULL indicates that the window is about to be
// destroyed.
gtk_widget_destroy(widget);
}
return;
}
for (GList *list = gdk_window_peek_children(aWindow);
list;
list = list->next) {
SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget);
}
gdk_window_set_user_data(aWindow, aNewWidget);
}
NS_IMETHODIMP
nsWindow::Destroy(void)
{
@ -724,6 +800,24 @@ nsWindow::Destroy(void)
mDragLeaveTimer = nsnull;
}
GtkWidget *owningWidget = GetMozContainerWidget();
if (mShell) {
gtk_widget_destroy(mShell);
mShell = nsnull;
mContainer = nsnull;
}
else if (mContainer) {
gtk_widget_destroy(GTK_WIDGET(mContainer));
mContainer = nsnull;
}
else if (owningWidget) {
// Remove references from GdkWindows back to their container
// widget while the GdkWindow hierarchy is still available.
// (OnContainerUnrealize does this when the MozContainer widget is
// destroyed.)
SetWidgetForHierarchy(mDrawingarea->clip_window, owningWidget, NULL);
}
if (mDrawingarea) {
g_object_set_data(G_OBJECT(mDrawingarea->clip_window),
"nsWindow", NULL);
@ -735,18 +829,15 @@ nsWindow::Destroy(void)
g_object_set_data(G_OBJECT(mDrawingarea->inner_window),
"mozdrawingarea", NULL);
NS_ASSERTION(!get_gtk_widget_for_gdk_window(mDrawingarea->inner_window),
"widget reference not removed");
g_object_unref(mDrawingarea);
mDrawingarea = nsnull;
}
if (mShell) {
gtk_widget_destroy(mShell);
mShell = nsnull;
mContainer = nsnull;
}
else if (mContainer) {
gtk_widget_destroy(GTK_WIDGET(mContainer));
mContainer = nsnull;
if (gInvisibleContainer && owningWidget == gInvisibleContainer) {
CheckDestroyInvisibleContainer();
}
OnDestroy();
@ -769,29 +860,63 @@ nsWindow::GetParent(void)
NS_IMETHODIMP
nsWindow::SetParent(nsIWidget *aNewParent)
{
NS_ENSURE_ARG_POINTER(aNewParent);
GdkWindow* newParentWindow =
static_cast<GdkWindow*>(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
NS_ASSERTION(newParentWindow, "Parent widget has a null native window handle");
if (!mShell && mDrawingarea) {
#ifdef DEBUG
if (!mContainer) {
// Check that the new Parent window has the same MozContainer
gpointer old_container;
gdk_window_get_user_data(mDrawingarea->inner_window,
&old_container);
gpointer new_container;
gdk_window_get_user_data(newParentWindow, &new_container);
NS_ASSERTION(old_container == new_container,
"FIXME: Wrong MozContainer on MozDrawingarea");
}
#endif
moz_drawingarea_reparent(mDrawingarea, newParentWindow);
} else {
if (mContainer || !mDrawingarea || !mParent) {
NS_NOTREACHED("nsWindow::SetParent - reparenting a non-child window");
return NS_ERROR_NOT_IMPLEMENTED;
}
// nsBaseWidget::SetZIndex adds child widgets to the parent's list.
nsCOMPtr<nsIWidget> kungFuDeathGrip = this;
mParent->RemoveChild(this);
mParent = aNewParent;
GtkWidget* oldContainer = GetMozContainerWidget();
if (!oldContainer) {
// The GdkWindows have been destroyed so there is nothing else to
// reparent.
NS_ABORT_IF_FALSE(GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed,
"live GdkWindow with no widget");
return NS_OK;
}
NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed,
"destroyed GdkWindow with widget");
GdkWindow* newParentWindow = NULL;
GtkWidget* newContainer = NULL;
if (aNewParent) {
newParentWindow = static_cast<GdkWindow*>
(aNewParent->GetNativeData(NS_NATIVE_WINDOW));
if (newParentWindow) {
newContainer = get_gtk_widget_for_gdk_window(newParentWindow);
}
} else {
// aNewParent is NULL, but reparent to a hidden window to avoid
// destroying the GdkWindow and its descendants.
// An invisible container widget is needed to hold descendant
// GtkWidgets.
newContainer = EnsureInvisibleContainer();
newParentWindow = newContainer->window;
}
if (!newContainer) {
// The new parent GdkWindow has been destroyed.
NS_ABORT_IF_FALSE(!newParentWindow ||
GDK_WINDOW_OBJECT(newParentWindow)->destroyed,
"live GdkWindow with no widget");
Destroy();
} else {
if (newContainer != oldContainer) {
NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(newParentWindow)->destroyed,
"destroyed GdkWindow with widget");
SetWidgetForHierarchy(mDrawingarea->clip_window, oldContainer,
newContainer);
}
moz_drawingarea_reparent(mDrawingarea, newParentWindow);
}
return NS_OK;
}
@ -2336,6 +2461,21 @@ nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent)
return FALSE;
}
void
nsWindow::OnContainerUnrealize(GtkWidget *aWidget)
{
// The GdkWindows are about to be destroyed (but not deleted), so remove
// their references back to their container widget while the GdkWindow
// hierarchy is still available.
NS_ASSERTION(mContainer == MOZ_CONTAINER(aWidget),
"unexpected \"unrealize\" signal");
if (mDrawingarea) {
SetWidgetForHierarchy(mDrawingarea->clip_window, aWidget, NULL);
}
}
void
nsWindow::OnSizeAllocate(GtkWidget *aWidget, GtkAllocation *aAllocation)
{
@ -3907,6 +4047,8 @@ nsWindow::NativeCreate(nsIWidget *aParent,
}
if (mContainer) {
g_signal_connect(G_OBJECT(mContainer), "unrealize",
G_CALLBACK(container_unrealize_cb), NULL);
g_signal_connect_after(G_OBJECT(mContainer), "size_allocate",
G_CALLBACK(size_allocate_cb), NULL);
g_signal_connect(G_OBJECT(mContainer), "expose_event",
@ -5224,6 +5366,17 @@ configure_event_cb(GtkWidget *widget,
return window->OnConfigureEvent(widget, event);
}
/* static */
void
container_unrealize_cb (GtkWidget *widget)
{
nsRefPtr<nsWindow> window = get_window_for_gtk_widget(widget);
if (!window)
return;
window->OnContainerUnrealize(widget);
}
/* static */
void
size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation)

Просмотреть файл

@ -219,6 +219,7 @@ public:
GdkEventExpose *aEvent);
gboolean OnConfigureEvent(GtkWidget *aWidget,
GdkEventConfigure *aEvent);
void OnContainerUnrealize(GtkWidget *aWidget);
void OnSizeAllocate(GtkWidget *aWidget,
GtkAllocation *aAllocation);
void OnDeleteEvent(GtkWidget *aWidget,