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