- add sources.
[platform/framework/web/crosswalk.git] / src / ui / base / gtk / gtk_floating_container.cc
1 // Copyright (c) 2011 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/base/gtk/gtk_floating_container.h"
6
7 #include <gtk/gtk.h>
8
9 #include <algorithm>
10
11 #include "ui/gfx/gtk_compat.h"
12
13 namespace {
14
15 enum {
16   SET_FLOATING_POSITION,
17   LAST_SIGNAL
18 };
19
20 enum {
21   CHILD_PROP_0,
22   CHILD_PROP_X,
23   CHILD_PROP_Y
24 };
25
26 // Returns the GtkFloatingContainerChild associated with |widget| (or NULL if
27 // |widget| not found).
28 GtkFloatingContainerChild* GetChild(GtkFloatingContainer* container,
29                                     GtkWidget* widget) {
30   for (GList* floating_children = container->floating_children;
31        floating_children; floating_children = g_list_next(floating_children)) {
32     GtkFloatingContainerChild* child =
33         reinterpret_cast<GtkFloatingContainerChild*>(floating_children->data);
34
35     if (child->widget == widget)
36       return child;
37   }
38
39   return NULL;
40 }
41
42 const GParamFlags kStaticReadWriteProp = static_cast<GParamFlags>(
43     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
44
45 }  // namespace
46
47 G_BEGIN_DECLS
48
49 static void gtk_floating_container_remove(GtkContainer* container,
50                                           GtkWidget* widget);
51 static void gtk_floating_container_forall(GtkContainer* container,
52                                           gboolean include_internals,
53                                           GtkCallback callback,
54                                           gpointer callback_data);
55 static void gtk_floating_container_size_request(GtkWidget* widget,
56                                                 GtkRequisition* requisition);
57 static void gtk_floating_container_size_allocate(GtkWidget* widget,
58                                                  GtkAllocation* allocation);
59 static void gtk_floating_container_set_child_property(GtkContainer* container,
60                                                       GtkWidget* child,
61                                                       guint property_id,
62                                                       const GValue* value,
63                                                       GParamSpec* pspec);
64 static void gtk_floating_container_get_child_property(GtkContainer* container,
65                                                       GtkWidget* child,
66                                                       guint property_id,
67                                                       GValue* value,
68                                                       GParamSpec* pspec);
69
70 static guint floating_container_signals[LAST_SIGNAL] = { 0 };
71
72 G_DEFINE_TYPE(GtkFloatingContainer, gtk_floating_container, GTK_TYPE_BIN)
73
74 static void gtk_floating_container_class_init(
75     GtkFloatingContainerClass *klass) {
76   GtkObjectClass* object_class =
77       reinterpret_cast<GtkObjectClass*>(klass);
78
79   GtkWidgetClass* widget_class =
80       reinterpret_cast<GtkWidgetClass*>(klass);
81   widget_class->size_request = gtk_floating_container_size_request;
82   widget_class->size_allocate = gtk_floating_container_size_allocate;
83
84   GtkContainerClass* container_class =
85       reinterpret_cast<GtkContainerClass*>(klass);
86   container_class->remove = gtk_floating_container_remove;
87   container_class->forall = gtk_floating_container_forall;
88
89   container_class->set_child_property =
90       gtk_floating_container_set_child_property;
91   container_class->get_child_property =
92       gtk_floating_container_get_child_property;
93
94   gtk_container_class_install_child_property(
95       container_class,
96       CHILD_PROP_X,
97       g_param_spec_int("x",
98                        "X position",
99                        "X position of child widget",
100                        G_MININT,
101                        G_MAXINT,
102                        0,
103                        kStaticReadWriteProp));
104
105   gtk_container_class_install_child_property(
106       container_class,
107       CHILD_PROP_Y,
108       g_param_spec_int("y",
109                        "Y position",
110                        "Y position of child widget",
111                        G_MININT,
112                        G_MAXINT,
113                        0,
114                        kStaticReadWriteProp));
115
116   floating_container_signals[SET_FLOATING_POSITION] =
117       g_signal_new("set-floating-position",
118                    G_OBJECT_CLASS_TYPE(object_class),
119                    static_cast<GSignalFlags>(G_SIGNAL_RUN_FIRST |
120                                              G_SIGNAL_ACTION),
121                    0,
122                    NULL, NULL,
123                    g_cclosure_marshal_VOID__BOXED,
124                    G_TYPE_NONE, 1,
125                    GDK_TYPE_RECTANGLE | G_SIGNAL_TYPE_STATIC_SCOPE);
126 }
127
128 static void gtk_floating_container_init(GtkFloatingContainer* container) {
129   gtk_widget_set_has_window(GTK_WIDGET(container), FALSE);
130   container->floating_children = NULL;
131 }
132
133 static void gtk_floating_container_remove(GtkContainer* container,
134                                           GtkWidget* widget) {
135   g_return_if_fail(GTK_IS_WIDGET(widget));
136
137   GtkBin* bin = GTK_BIN(container);
138   if (gtk_bin_get_child(bin) == widget) {
139     ((GTK_CONTAINER_CLASS(gtk_floating_container_parent_class))->remove)
140         (container, widget);
141   } else {
142     // Handle the other case where it's in our |floating_children| list.
143     GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container);
144     GList* children = floating->floating_children;
145     gboolean removed_child = false;
146     while (children) {
147       GtkFloatingContainerChild* child =
148           reinterpret_cast<GtkFloatingContainerChild*>(children->data);
149
150       if (child->widget == widget) {
151         removed_child = true;
152         gboolean was_visible = gtk_widget_get_visible(GTK_WIDGET(widget));
153
154         gtk_widget_unparent(widget);
155
156         floating->floating_children =
157             g_list_remove_link(floating->floating_children, children);
158         g_list_free(children);
159         g_free(child);
160
161         if (was_visible && gtk_widget_get_visible(GTK_WIDGET(container)))
162           gtk_widget_queue_resize(GTK_WIDGET(container));
163
164         break;
165       }
166       children = children->next;
167     }
168
169     g_return_if_fail(removed_child);
170   }
171 }
172
173 static void gtk_floating_container_forall(GtkContainer* container,
174                                           gboolean include_internals,
175                                           GtkCallback callback,
176                                           gpointer callback_data) {
177   g_return_if_fail(container != NULL);
178   g_return_if_fail(callback != NULL);
179
180   // Let GtkBin do its part of the forall.
181   ((GTK_CONTAINER_CLASS(gtk_floating_container_parent_class))->forall)
182       (container, include_internals, callback, callback_data);
183
184   GtkFloatingContainer* floating = GTK_FLOATING_CONTAINER(container);
185   GList* children = floating->floating_children;
186   while (children) {
187     GtkFloatingContainerChild* child =
188         reinterpret_cast<GtkFloatingContainerChild*>(children->data);
189     children = children->next;
190
191     (*callback)(child->widget, callback_data);
192   }
193 }
194
195 static void gtk_floating_container_size_request(GtkWidget* widget,
196                                                 GtkRequisition* requisition) {
197   GtkBin* bin = GTK_BIN(widget);
198   if (bin && gtk_bin_get_child(bin)) {
199     gtk_widget_size_request(gtk_bin_get_child(bin), requisition);
200   } else {
201     requisition->width = 0;
202     requisition->height = 0;
203   }
204 }
205
206 static void gtk_floating_container_size_allocate(GtkWidget* widget,
207                                                  GtkAllocation* allocation) {
208   gtk_widget_set_allocation(widget, allocation);
209
210   if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) {
211     gdk_window_move_resize(gtk_widget_get_window(widget),
212                            allocation->x,
213                            allocation->y,
214                            allocation->width,
215                            allocation->height);
216   }
217
218   // Give the same allocation to our GtkBin component.
219   GtkBin* bin = GTK_BIN(widget);
220   if (gtk_bin_get_child(bin)) {
221     gtk_widget_size_allocate(gtk_bin_get_child(bin), allocation);
222   }
223
224   // We need to give whoever is pulling our strings a chance to set the "x" and
225   // "y" properties on all of our children.
226   g_signal_emit(widget, floating_container_signals[SET_FLOATING_POSITION], 0,
227                 allocation);
228
229   // Our allocation has been set. We've asked our controller to place the other
230   // widgets. Pass out allocations to all our children based on where they want
231   // to be.
232   GtkFloatingContainer* container = GTK_FLOATING_CONTAINER(widget);
233   GList* children = container->floating_children;
234   GtkAllocation child_allocation;
235   GtkRequisition child_requisition;
236   while (children) {
237     GtkFloatingContainerChild* child =
238         reinterpret_cast<GtkFloatingContainerChild*>(children->data);
239     children = children->next;
240
241     if (gtk_widget_get_visible(GTK_WIDGET(child->widget))) {
242       gtk_widget_size_request(child->widget, &child_requisition);
243       child_allocation.x = allocation->x + child->x;
244       child_allocation.y = allocation->y + child->y;
245       child_allocation.width = std::max(1, std::min(child_requisition.width,
246                                                     allocation->width));
247       child_allocation.height = std::max(1, std::min(child_requisition.height,
248                                                      allocation->height));
249       gtk_widget_size_allocate(child->widget, &child_allocation);
250     }
251   }
252 }
253
254 static void gtk_floating_container_set_child_property(GtkContainer* container,
255                                                       GtkWidget* child,
256                                                       guint property_id,
257                                                       const GValue* value,
258                                                       GParamSpec* pspec) {
259   GtkFloatingContainerChild* floating_child =
260       GetChild(GTK_FLOATING_CONTAINER(container), child);
261   g_return_if_fail(floating_child);
262
263   switch (property_id) {
264     case CHILD_PROP_X:
265       floating_child->x = g_value_get_int(value);
266       gtk_widget_child_notify(child, "x");
267       break;
268     case CHILD_PROP_Y:
269       floating_child->y = g_value_get_int(value);
270       gtk_widget_child_notify(child, "y");
271       break;
272     default:
273       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(
274           container, property_id, pspec);
275       break;
276   };
277 }
278
279 static void gtk_floating_container_get_child_property(GtkContainer* container,
280                                                       GtkWidget* child,
281                                                       guint property_id,
282                                                       GValue* value,
283                                                       GParamSpec* pspec) {
284   GtkFloatingContainerChild* floating_child =
285       GetChild(GTK_FLOATING_CONTAINER(container), child);
286   g_return_if_fail(floating_child);
287
288   switch (property_id) {
289     case CHILD_PROP_X:
290       g_value_set_int(value, floating_child->x);
291       break;
292     case CHILD_PROP_Y:
293       g_value_set_int(value, floating_child->y);
294       break;
295     default:
296       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID(
297           container, property_id, pspec);
298       break;
299   };
300 }
301
302 GtkWidget* gtk_floating_container_new() {
303   return GTK_WIDGET(g_object_new(GTK_TYPE_FLOATING_CONTAINER, NULL));
304 }
305
306 void gtk_floating_container_add_floating(GtkFloatingContainer* container,
307                                          GtkWidget* widget) {
308   g_return_if_fail(GTK_IS_FLOATING_CONTAINER(container));
309   g_return_if_fail(GTK_IS_WIDGET(widget));
310
311   GtkFloatingContainerChild* child_info = g_new(GtkFloatingContainerChild, 1);
312   child_info->widget = widget;
313   child_info->x = 0;
314   child_info->y = 0;
315
316   gtk_widget_set_parent(widget, GTK_WIDGET(container));
317
318   container->floating_children =
319       g_list_append(container->floating_children, child_info);
320 }
321
322 G_END_DECLS