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