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 13namespace { 14 15enum { 16 SET_FLOATING_POSITION, 17 LAST_SIGNAL 18}; 19 20enum { 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). 28GtkFloatingContainerChild* 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 44G_BEGIN_DECLS 45 46static void gtk_floating_container_remove(GtkContainer* container, 47 GtkWidget* widget); 48static void gtk_floating_container_forall(GtkContainer* container, 49 gboolean include_internals, 50 GtkCallback callback, 51 gpointer callback_data); 52static void gtk_floating_container_size_request(GtkWidget* widget, 53 GtkRequisition* requisition); 54static void gtk_floating_container_size_allocate(GtkWidget* widget, 55 GtkAllocation* allocation); 56static void gtk_floating_container_set_child_property(GtkContainer* container, 57 GtkWidget* child, 58 guint property_id, 59 const GValue* value, 60 GParamSpec* pspec); 61static void gtk_floating_container_get_child_property(GtkContainer* container, 62 GtkWidget* child, 63 guint property_id, 64 GValue* value, 65 GParamSpec* pspec); 66 67static guint floating_container_signals[LAST_SIGNAL] = { 0 }; 68 69G_DEFINE_TYPE(GtkFloatingContainer, gtk_floating_container, GTK_TYPE_BIN) 70 71static 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 125static void gtk_floating_container_init(GtkFloatingContainer* container) { 126 GTK_WIDGET_SET_FLAGS(container, GTK_NO_WINDOW); 127 128 container->floating_children = NULL; 129} 130 131static 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 171static 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 193static 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 204static 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 252static 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 277static 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 300GtkWidget* gtk_floating_container_new() { 301 return GTK_WIDGET(g_object_new(GTK_TYPE_FLOATING_CONTAINER, NULL)); 302} 303 304void 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 320G_END_DECLS 321