• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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