1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/gfx/gtk_preserve_window.h" 6 7 #include <gdk/gdk.h> 8 #include <gtk/gtk.h> 9 10 #include "ui/gfx/gtk_compat.h" 11 12 G_BEGIN_DECLS 13 14 #define GTK_PRESERVE_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ 15 GTK_TYPE_PRESERVE_WINDOW, \ 16 GtkPreserveWindowPrivate)) 17 18 typedef struct _GtkPreserveWindowPrivate GtkPreserveWindowPrivate; 19 20 struct _GtkPreserveWindowPrivate { 21 // If true, don't create/destroy windows on realize/unrealize. 22 gboolean preserve_window; 23 24 // Whether or not we delegate the resize of the GdkWindow 25 // to someone else. 26 gboolean delegate_resize; 27 28 // Accessible factory and userdata. 29 AtkObject* (*accessible_factory)(void* userdata); 30 void* accessible_factory_userdata; 31 }; 32 33 G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED) 34 35 static void gtk_preserve_window_destroy(GtkObject* object); 36 static void gtk_preserve_window_realize(GtkWidget* widget); 37 static void gtk_preserve_window_unrealize(GtkWidget* widget); 38 static void gtk_preserve_window_size_allocate(GtkWidget* widget, 39 GtkAllocation* allocation); 40 static AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget); 41 42 static void gtk_preserve_window_class_init(GtkPreserveWindowClass *klass) { 43 GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass); 44 widget_class->realize = gtk_preserve_window_realize; 45 widget_class->unrealize = gtk_preserve_window_unrealize; 46 widget_class->size_allocate = gtk_preserve_window_size_allocate; 47 widget_class->get_accessible = gtk_preserve_window_get_accessible; 48 49 GtkObjectClass* object_class = reinterpret_cast<GtkObjectClass*>(klass); 50 object_class->destroy = gtk_preserve_window_destroy; 51 52 GObjectClass* gobject_class = G_OBJECT_CLASS(klass); 53 g_type_class_add_private(gobject_class, sizeof(GtkPreserveWindowPrivate)); 54 } 55 56 static void gtk_preserve_window_init(GtkPreserveWindow* widget) { 57 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 58 priv->preserve_window = FALSE; 59 priv->accessible_factory = NULL; 60 priv->accessible_factory_userdata = NULL; 61 62 // These widgets always have their own window. 63 gtk_widget_set_has_window(GTK_WIDGET(widget), TRUE); 64 } 65 66 GtkWidget* gtk_preserve_window_new() { 67 return GTK_WIDGET(g_object_new(GTK_TYPE_PRESERVE_WINDOW, NULL)); 68 } 69 70 static void gtk_preserve_window_destroy(GtkObject* object) { 71 GtkWidget* widget = reinterpret_cast<GtkWidget*>(object); 72 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 73 74 GdkWindow* gdk_window = gtk_widget_get_window(widget); 75 if (gdk_window) { 76 gdk_window_set_user_data(gdk_window, NULL); 77 // If the window is preserved, someone else must destroy it. 78 if (!priv->preserve_window) 79 gdk_window_destroy(gdk_window); 80 gtk_widget_set_window(widget, NULL); 81 } 82 83 GTK_OBJECT_CLASS(gtk_preserve_window_parent_class)->destroy(object); 84 } 85 86 static void gtk_preserve_window_realize(GtkWidget* widget) { 87 g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); 88 89 GdkWindow* gdk_window = gtk_widget_get_window(widget); 90 if (gdk_window) { 91 GtkAllocation allocation; 92 gtk_widget_get_allocation(widget, &allocation); 93 94 gdk_window_reparent(gdk_window, 95 gtk_widget_get_parent_window(widget), 96 allocation.x, 97 allocation.y); 98 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 99 if (!priv->delegate_resize) { 100 gdk_window_resize(gdk_window, 101 allocation.width, 102 allocation.height); 103 } 104 gint event_mask = gtk_widget_get_events(widget); 105 event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; 106 gdk_window_set_events(gdk_window, (GdkEventMask) event_mask); 107 gdk_window_set_user_data(gdk_window, widget); 108 109 gtk_widget_set_realized(widget, TRUE); 110 111 gtk_widget_style_attach(widget); 112 gtk_style_set_background(gtk_widget_get_style(widget), 113 gdk_window, GTK_STATE_NORMAL); 114 } else { 115 GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->realize(widget); 116 } 117 } 118 119 static void gtk_preserve_window_unrealize(GtkWidget* widget) { 120 g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); 121 122 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 123 if (priv->preserve_window) { 124 GtkWidgetClass* widget_class = 125 GTK_WIDGET_CLASS(gtk_preserve_window_parent_class); 126 GtkContainerClass* container_class = 127 GTK_CONTAINER_CLASS(gtk_preserve_window_parent_class); 128 129 if (gtk_widget_get_mapped(widget)) { 130 widget_class->unmap(widget); 131 132 gtk_widget_set_mapped(widget, FALSE); 133 } 134 135 // This is the behavior from GtkWidget, inherited by GtkFixed. 136 // It is unclear why we should not call the potentially overridden 137 // unrealize method (via the callback), but doing so causes errors. 138 container_class->forall( 139 GTK_CONTAINER(widget), FALSE, 140 reinterpret_cast<GtkCallback>(gtk_widget_unrealize), NULL); 141 142 GdkWindow* gdk_window = gtk_widget_get_window(widget); 143 144 // TODO(erg): Almost all style handling will need to be overhauled in GTK3. 145 gtk_style_detach(gtk_widget_get_style(widget)); 146 gdk_window_reparent(gdk_window, gdk_get_default_root_window(), 0, 0); 147 gtk_selection_remove_all(widget); 148 gdk_window_set_user_data(gdk_window, NULL); 149 150 gtk_widget_set_realized(widget, FALSE); 151 } else { 152 GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->unrealize(widget); 153 } 154 } 155 156 gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* window) { 157 g_return_val_if_fail(GTK_IS_PRESERVE_WINDOW(window), FALSE); 158 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window); 159 160 return priv->preserve_window; 161 } 162 163 void gtk_preserve_window_set_preserve(GtkPreserveWindow* window, 164 gboolean value) { 165 g_return_if_fail(GTK_IS_PRESERVE_WINDOW(window)); 166 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window); 167 priv->preserve_window = value; 168 169 GtkWidget* widget = GTK_WIDGET(window); 170 GdkWindow* gdk_window = gtk_widget_get_window(widget); 171 if (value && !gdk_window) { 172 GdkWindowAttr attributes; 173 gint attributes_mask; 174 175 // We may not know the width and height, so we rely on the fact 176 // that a size-allocation will resize it later. 177 attributes.width = 1; 178 attributes.height = 1; 179 180 attributes.window_type = GDK_WINDOW_CHILD; 181 attributes.wclass = GDK_INPUT_OUTPUT; 182 attributes.override_redirect = TRUE; 183 184 attributes.visual = gtk_widget_get_visual(widget); 185 attributes.colormap = gtk_widget_get_colormap(widget); 186 187 attributes.event_mask = gtk_widget_get_events(widget); 188 attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; 189 190 attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR; 191 gdk_window = gdk_window_new( 192 gdk_get_default_root_window(), &attributes, attributes_mask); 193 gtk_widget_set_window(widget, gdk_window); 194 } else if (!value && gdk_window && !gtk_widget_get_realized(widget)) { 195 gdk_window_destroy(gdk_window); 196 gtk_widget_set_window(widget, NULL); 197 } 198 } 199 200 void gtk_preserve_window_size_allocate(GtkWidget* widget, 201 GtkAllocation* allocation) { 202 g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget)); 203 204 gtk_widget_set_allocation(widget, allocation); 205 206 if (gtk_widget_get_realized(widget)) { 207 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 208 GdkWindow* gdk_window = gtk_widget_get_window(widget); 209 if (priv->delegate_resize) { 210 gdk_window_move(gdk_window, allocation->x, allocation->y); 211 } else { 212 gdk_window_move_resize( 213 gdk_window, allocation->x, allocation->y, 214 allocation->width, allocation->height); 215 } 216 } 217 218 // Propagate resize to children 219 guint16 border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); 220 GList *children = GTK_FIXED(widget)->children; 221 while (children) { 222 GtkFixedChild *child = reinterpret_cast<GtkFixedChild*>(children->data); 223 if (gtk_widget_get_visible(child->widget)) { 224 GtkRequisition child_requisition; 225 gtk_widget_get_child_requisition(child->widget, &child_requisition); 226 227 GtkAllocation child_allocation; 228 child_allocation.x = child->x + border_width; 229 child_allocation.y = child->y + border_width; 230 child_allocation.width = child_requisition.width; 231 child_allocation.height = child_requisition.height; 232 233 gtk_widget_size_allocate(child->widget, &child_allocation); 234 } 235 children = children->next; 236 } 237 } 238 239 void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget, 240 gboolean delegate) { 241 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 242 priv->delegate_resize = delegate; 243 } 244 245 void gtk_preserve_window_set_accessible_factory( 246 GtkPreserveWindow* widget, 247 AtkObject* (*factory)(void* userdata), 248 gpointer userdata) { 249 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 250 priv->accessible_factory = factory; 251 priv->accessible_factory_userdata = userdata; 252 } 253 254 AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget) { 255 GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget); 256 if (priv->accessible_factory) { 257 return priv->accessible_factory(priv->accessible_factory_userdata); 258 } else { 259 return GTK_WIDGET_CLASS(gtk_preserve_window_parent_class) 260 ->get_accessible(widget); 261 } 262 } 263 264 G_END_DECLS 265