1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:expandtab:shiftwidth=4:tabstop=4:
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "MozContainer.h"
9
10 #include <glib.h>
11 #include <gtk/gtk.h>
12 #include <gdk/gdkx.h>
13 #include <stdio.h>
14
15 #ifdef ACCESSIBILITY
16 # include <atk/atk.h>
17 # include "maiRedundantObjectFactory.h"
18 #endif
19
20 #ifdef MOZ_LOGGING
21 # include "mozilla/Logging.h"
22 # include "nsTArray.h"
23 # include "Units.h"
24 extern mozilla::LazyLogModule gWidgetLog;
25 # define LOGCONTAINER(args) MOZ_LOG(gWidgetLog, mozilla::LogLevel::Debug, args)
26 #else
27 # define LOGCONTAINER(args)
28 #endif /* MOZ_LOGGING */
29
30 /* init methods */
31 static void moz_container_class_init(MozContainerClass* klass);
32 static void moz_container_init(MozContainer* container);
33
34 /* widget class methods */
35 static void moz_container_map(GtkWidget* widget);
36 static void moz_container_unmap(GtkWidget* widget);
37 static void moz_container_size_allocate(GtkWidget* widget,
38 GtkAllocation* allocation);
39 void moz_container_realize(GtkWidget* widget);
40
41 /* container class methods */
42 static void moz_container_remove(GtkContainer* container,
43 GtkWidget* child_widget);
44 static void moz_container_forall(GtkContainer* container,
45 gboolean include_internals,
46 GtkCallback callback, gpointer callback_data);
47 static void moz_container_add(GtkContainer* container, GtkWidget* widget);
48
49 typedef struct _MozContainerChild MozContainerChild;
50
51 struct _MozContainerChild {
52 GtkWidget* widget;
53 gint x;
54 gint y;
55 };
56
57 static void moz_container_allocate_child(MozContainer* container,
58 MozContainerChild* child);
59 static MozContainerChild* moz_container_get_child(MozContainer* container,
60 GtkWidget* child);
61
62 /* public methods */
63
moz_container_get_type(void)64 GType moz_container_get_type(void) {
65 static GType moz_container_type = 0;
66
67 if (!moz_container_type) {
68 static GTypeInfo moz_container_info = {
69 sizeof(MozContainerClass), /* class_size */
70 NULL, /* base_init */
71 NULL, /* base_finalize */
72 (GClassInitFunc)moz_container_class_init, /* class_init */
73 NULL, /* class_destroy */
74 NULL, /* class_data */
75 sizeof(MozContainer), /* instance_size */
76 0, /* n_preallocs */
77 (GInstanceInitFunc)moz_container_init, /* instance_init */
78 NULL, /* value_table */
79 };
80
81 #ifdef MOZ_WAYLAND
82 if (GdkIsWaylandDisplay()) {
83 moz_container_info.class_init =
84 (GClassInitFunc)moz_container_wayland_class_init;
85 }
86 #endif
87
88 moz_container_type =
89 g_type_register_static(GTK_TYPE_CONTAINER, "MozContainer",
90 &moz_container_info, static_cast<GTypeFlags>(0));
91 #ifdef ACCESSIBILITY
92 /* Set a factory to return accessible object with ROLE_REDUNDANT for
93 * MozContainer, so that gail won't send focus notification for it */
94 atk_registry_set_factory_type(atk_get_default_registry(),
95 moz_container_type,
96 mai_redundant_object_factory_get_type());
97 #endif
98 }
99
100 return moz_container_type;
101 }
102
moz_container_new(void)103 GtkWidget* moz_container_new(void) {
104 MozContainer* container;
105
106 container =
107 static_cast<MozContainer*>(g_object_new(MOZ_CONTAINER_TYPE, nullptr));
108
109 return GTK_WIDGET(container);
110 }
111
moz_container_put(MozContainer * container,GtkWidget * child_widget,gint x,gint y)112 void moz_container_put(MozContainer* container, GtkWidget* child_widget, gint x,
113 gint y) {
114 MozContainerChild* child;
115
116 child = g_new(MozContainerChild, 1);
117
118 child->widget = child_widget;
119 child->x = x;
120 child->y = y;
121
122 /* printf("moz_container_put %p %p %d %d\n", (void *)container,
123 (void *)child_widget, x, y); */
124
125 container->children = g_list_append(container->children, child);
126
127 /* we assume that the caller of this function will have already set
128 the parent GdkWindow because we can have many anonymous children. */
129 gtk_widget_set_parent(child_widget, GTK_WIDGET(container));
130 }
131
moz_container_class_init(MozContainerClass * klass)132 void moz_container_class_init(MozContainerClass* klass) {
133 /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
134 GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */
135 GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass);
136 GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
137
138 widget_class->map = moz_container_map;
139 widget_class->unmap = moz_container_unmap;
140 widget_class->realize = moz_container_realize;
141 widget_class->size_allocate = moz_container_size_allocate;
142
143 container_class->remove = moz_container_remove;
144 container_class->forall = moz_container_forall;
145 container_class->add = moz_container_add;
146 }
147
moz_container_init(MozContainer * container)148 void moz_container_init(MozContainer* container) {
149 gtk_widget_set_can_focus(GTK_WIDGET(container), TRUE);
150 gtk_container_set_resize_mode(GTK_CONTAINER(container), GTK_RESIZE_IMMEDIATE);
151 gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE);
152 #ifdef MOZ_WAYLAND
153 if (GdkIsWaylandDisplay()) {
154 moz_container_wayland_init(&container->wl_container);
155 }
156 #endif
157 LOGCONTAINER(("%s [%p]\n", __FUNCTION__, (void*)container));
158 }
159
moz_container_map(GtkWidget * widget)160 void moz_container_map(GtkWidget* widget) {
161 MozContainer* container;
162 GList* tmp_list;
163 GtkWidget* tmp_child;
164
165 g_return_if_fail(IS_MOZ_CONTAINER(widget));
166 container = MOZ_CONTAINER(widget);
167
168 gtk_widget_set_mapped(widget, TRUE);
169
170 tmp_list = container->children;
171 while (tmp_list) {
172 tmp_child = ((MozContainerChild*)tmp_list->data)->widget;
173
174 if (gtk_widget_get_visible(tmp_child)) {
175 if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child);
176 }
177 tmp_list = tmp_list->next;
178 }
179
180 if (gtk_widget_get_has_window(widget)) {
181 gdk_window_show(gtk_widget_get_window(widget));
182 }
183 }
184
moz_container_unmap(GtkWidget * widget)185 void moz_container_unmap(GtkWidget* widget) {
186 g_return_if_fail(IS_MOZ_CONTAINER(widget));
187
188 gtk_widget_set_mapped(widget, FALSE);
189
190 if (gtk_widget_get_has_window(widget)) {
191 gdk_window_hide(gtk_widget_get_window(widget));
192 }
193 }
194
moz_container_realize(GtkWidget * widget)195 void moz_container_realize(GtkWidget* widget) {
196 GdkWindow* parent = gtk_widget_get_parent_window(widget);
197 GdkWindow* window;
198
199 gtk_widget_set_realized(widget, TRUE);
200
201 if (gtk_widget_get_has_window(widget)) {
202 GdkWindowAttr attributes;
203 gint attributes_mask = GDK_WA_VISUAL | GDK_WA_X | GDK_WA_Y;
204 GtkAllocation allocation;
205
206 gtk_widget_get_allocation(widget, &allocation);
207 attributes.event_mask = gtk_widget_get_events(widget);
208 attributes.x = allocation.x;
209 attributes.y = allocation.y;
210 attributes.width = allocation.width;
211 attributes.height = allocation.height;
212 attributes.wclass = GDK_INPUT_OUTPUT;
213 attributes.window_type = GDK_WINDOW_CHILD;
214 MozContainer* container = MOZ_CONTAINER(widget);
215 attributes.visual =
216 container->force_default_visual
217 ? gdk_screen_get_system_visual(gtk_widget_get_screen(widget))
218 : gtk_widget_get_visual(widget);
219
220 window = gdk_window_new(parent, &attributes, attributes_mask);
221
222 LOGCONTAINER(("moz_container_realize() [%p] GdkWindow %p\n",
223 (void*)container, (void*)window));
224
225 gdk_window_set_user_data(window, widget);
226 } else {
227 window = parent;
228 g_object_ref(window);
229 }
230
231 gtk_widget_set_window(widget, window);
232 }
233
moz_container_size_allocate(GtkWidget * widget,GtkAllocation * allocation)234 void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) {
235 MozContainer* container;
236 GList* tmp_list;
237 GtkAllocation tmp_allocation;
238
239 g_return_if_fail(IS_MOZ_CONTAINER(widget));
240
241 LOGCONTAINER(("moz_container_size_allocate [%p] %d,%d -> %d x %d\n",
242 (void*)widget, allocation->x, allocation->y, allocation->width,
243 allocation->height));
244
245 /* short circuit if you can */
246 container = MOZ_CONTAINER(widget);
247 gtk_widget_get_allocation(widget, &tmp_allocation);
248 if (!container->children && tmp_allocation.x == allocation->x &&
249 tmp_allocation.y == allocation->y &&
250 tmp_allocation.width == allocation->width &&
251 tmp_allocation.height == allocation->height) {
252 return;
253 }
254
255 gtk_widget_set_allocation(widget, allocation);
256
257 tmp_list = container->children;
258
259 while (tmp_list) {
260 MozContainerChild* child = static_cast<MozContainerChild*>(tmp_list->data);
261
262 moz_container_allocate_child(container, child);
263
264 tmp_list = tmp_list->next;
265 }
266
267 if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
268 gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x,
269 allocation->y, allocation->width,
270 allocation->height);
271 }
272 }
273
moz_container_remove(GtkContainer * container,GtkWidget * child_widget)274 void moz_container_remove(GtkContainer* container, GtkWidget* child_widget) {
275 MozContainerChild* child;
276 MozContainer* moz_container;
277 GdkWindow* parent_window;
278
279 g_return_if_fail(IS_MOZ_CONTAINER(container));
280 g_return_if_fail(GTK_IS_WIDGET(child_widget));
281
282 moz_container = MOZ_CONTAINER(container);
283
284 child = moz_container_get_child(moz_container, child_widget);
285 g_return_if_fail(child);
286
287 /* gtk_widget_unparent will remove the parent window (as well as the
288 * parent widget), but, in Mozilla's window hierarchy, the parent window
289 * may need to be kept because it may be part of a GdkWindow sub-hierarchy
290 * that is being moved to another MozContainer.
291 *
292 * (In a conventional GtkWidget hierarchy, GdkWindows being reparented
293 * would have their own GtkWidget and that widget would be the one being
294 * reparented. In Mozilla's hierarchy, the parent_window needs to be
295 * retained so that the GdkWindow sub-hierarchy is maintained.)
296 */
297 parent_window = gtk_widget_get_parent_window(child_widget);
298 if (parent_window) g_object_ref(parent_window);
299
300 gtk_widget_unparent(child_widget);
301
302 if (parent_window) {
303 /* The child_widget will always still exist because g_signal_emit,
304 * which invokes this function, holds a reference.
305 *
306 * If parent_window is the container's root window then it will not be
307 * the parent_window if the child_widget is placed in another
308 * container.
309 */
310 if (parent_window != gtk_widget_get_window(GTK_WIDGET(container)))
311 gtk_widget_set_parent_window(child_widget, parent_window);
312
313 g_object_unref(parent_window);
314 }
315
316 moz_container->children = g_list_remove(moz_container->children, child);
317 g_free(child);
318 }
319
moz_container_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)320 void moz_container_forall(GtkContainer* container, gboolean include_internals,
321 GtkCallback callback, gpointer callback_data) {
322 MozContainer* moz_container;
323 GList* tmp_list;
324
325 g_return_if_fail(IS_MOZ_CONTAINER(container));
326 g_return_if_fail(callback != NULL);
327
328 moz_container = MOZ_CONTAINER(container);
329
330 tmp_list = moz_container->children;
331 while (tmp_list) {
332 MozContainerChild* child;
333 child = static_cast<MozContainerChild*>(tmp_list->data);
334 tmp_list = tmp_list->next;
335 (*callback)(child->widget, callback_data);
336 }
337 }
338
moz_container_allocate_child(MozContainer * container,MozContainerChild * child)339 static void moz_container_allocate_child(MozContainer* container,
340 MozContainerChild* child) {
341 GtkAllocation allocation;
342
343 gtk_widget_get_allocation(child->widget, &allocation);
344 allocation.x = child->x;
345 allocation.y = child->y;
346
347 gtk_widget_size_allocate(child->widget, &allocation);
348 }
349
moz_container_get_child(MozContainer * container,GtkWidget * child_widget)350 MozContainerChild* moz_container_get_child(MozContainer* container,
351 GtkWidget* child_widget) {
352 GList* tmp_list;
353
354 tmp_list = container->children;
355 while (tmp_list) {
356 MozContainerChild* child;
357
358 child = static_cast<MozContainerChild*>(tmp_list->data);
359 tmp_list = tmp_list->next;
360
361 if (child->widget == child_widget) return child;
362 }
363
364 return NULL;
365 }
366
moz_container_add(GtkContainer * container,GtkWidget * widget)367 static void moz_container_add(GtkContainer* container, GtkWidget* widget) {
368 moz_container_put(MOZ_CONTAINER(container), widget, 0, 0);
369 }
370
moz_container_force_default_visual(MozContainer * container)371 void moz_container_force_default_visual(MozContainer* container) {
372 container->force_default_visual = true;
373 }
374
375 #undef LOGCONTAINER
376