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_expanded_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 CHILD_SIZE_REQUEST, 17 LAST_SIGNAL 18 }; 19 20 guint expanded_container_signals[LAST_SIGNAL] = { 0 }; 21 22 struct SizeAllocateData { 23 GtkWidget* container; 24 GtkAllocation* allocation; 25 int border_width; 26 }; 27 28 void GetChildPosition(GtkWidget* container, GtkWidget* child, int* x, int* y) { 29 GValue v = { 0 }; 30 g_value_init(&v, G_TYPE_INT); 31 gtk_container_child_get_property(GTK_CONTAINER(container), child, "x", &v); 32 *x = g_value_get_int(&v); 33 gtk_container_child_get_property(GTK_CONTAINER(container), child, "y", &v); 34 *y = g_value_get_int(&v); 35 g_value_unset(&v); 36 } 37 38 void ChildSizeAllocate(GtkWidget* child, gpointer userdata) { 39 if (!gtk_widget_get_visible(child)) 40 return; 41 42 SizeAllocateData* data = reinterpret_cast<SizeAllocateData*>(userdata); 43 44 GtkRequisition child_requisition; 45 child_requisition.width = data->allocation->width - data->border_width * 2; 46 child_requisition.height = data->allocation->height - data->border_width * 2; 47 48 // We need to give whoever is pulling our strings a chance to adjust the 49 // size of our children. 50 g_signal_emit(data->container, 51 expanded_container_signals[CHILD_SIZE_REQUEST], 0, 52 child, &child_requisition); 53 54 GtkAllocation child_allocation; 55 child_allocation.width = child_requisition.width; 56 child_allocation.height = child_requisition.height; 57 if (child_allocation.width < 0 || child_allocation.height < 0) { 58 gtk_widget_get_child_requisition(child, &child_requisition); 59 if (child_allocation.width < 0) 60 child_allocation.width = child_requisition.width; 61 if (child_allocation.height < 0) 62 child_allocation.height = child_requisition.height; 63 } 64 65 int x, y; 66 GetChildPosition(data->container, child, &x, &y); 67 68 child_allocation.x = x + data->border_width; 69 child_allocation.y = y + data->border_width; 70 71 if (!gtk_widget_get_has_window(data->container)) { 72 child_allocation.x += data->allocation->x; 73 child_allocation.y += data->allocation->y; 74 } 75 gtk_widget_size_allocate(child, &child_allocation); 76 } 77 78 void Marshal_VOID__OBJECT_BOXED(GClosure* closure, 79 GValue* return_value G_GNUC_UNUSED, 80 guint n_param_values, 81 const GValue* param_values, 82 gpointer invocation_hint G_GNUC_UNUSED, 83 gpointer marshal_data) { 84 typedef void (*GMarshalFunc_VOID__OBJECT_BOXED) (gpointer data1, 85 gpointer arg_1, 86 gpointer arg_2, 87 gpointer data2); 88 register GMarshalFunc_VOID__OBJECT_BOXED callback; 89 register GCClosure *cc = reinterpret_cast<GCClosure*>(closure); 90 register gpointer data1, data2; 91 92 g_return_if_fail(n_param_values == 3); 93 94 if (G_CCLOSURE_SWAP_DATA(closure)) { 95 data1 = closure->data; 96 data2 = g_value_peek_pointer(param_values + 0); 97 } else { 98 data1 = g_value_peek_pointer(param_values + 0); 99 data2 = closure->data; 100 } 101 102 callback = reinterpret_cast<GMarshalFunc_VOID__OBJECT_BOXED>( 103 marshal_data ? marshal_data : cc->callback); 104 105 callback(data1, 106 g_value_get_object(param_values + 1), 107 g_value_get_boxed(param_values + 2), 108 data2); 109 } 110 111 } // namespace 112 113 G_BEGIN_DECLS 114 115 static void gtk_expanded_container_size_allocate(GtkWidget* widget, 116 GtkAllocation* allocation); 117 118 G_DEFINE_TYPE(GtkExpandedContainer, gtk_expanded_container, GTK_TYPE_FIXED) 119 120 static void gtk_expanded_container_class_init( 121 GtkExpandedContainerClass *klass) { 122 GtkObjectClass* object_class = 123 reinterpret_cast<GtkObjectClass*>(klass); 124 125 GtkWidgetClass* widget_class = 126 reinterpret_cast<GtkWidgetClass*>(klass); 127 widget_class->size_allocate = gtk_expanded_container_size_allocate; 128 129 expanded_container_signals[CHILD_SIZE_REQUEST] = 130 g_signal_new("child-size-request", 131 G_OBJECT_CLASS_TYPE(object_class), 132 static_cast<GSignalFlags>(G_SIGNAL_RUN_FIRST), 133 0, 134 NULL, NULL, 135 Marshal_VOID__OBJECT_BOXED, 136 G_TYPE_NONE, 2, 137 GTK_TYPE_WIDGET, 138 GTK_TYPE_REQUISITION | G_SIGNAL_TYPE_STATIC_SCOPE); 139 } 140 141 static void gtk_expanded_container_init(GtkExpandedContainer* container) { 142 } 143 144 static void gtk_expanded_container_size_allocate(GtkWidget* widget, 145 GtkAllocation* allocation) { 146 gtk_widget_set_allocation(widget, allocation); 147 148 if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) { 149 gdk_window_move_resize(gtk_widget_get_window(widget), 150 allocation->x, 151 allocation->y, 152 allocation->width, 153 allocation->height); 154 } 155 156 SizeAllocateData data; 157 data.container = widget; 158 data.allocation = allocation; 159 data.border_width = gtk_container_get_border_width(GTK_CONTAINER(widget)); 160 161 gtk_container_foreach(GTK_CONTAINER(widget), ChildSizeAllocate, &data); 162 } 163 164 GtkWidget* gtk_expanded_container_new() { 165 return GTK_WIDGET(g_object_new(GTK_TYPE_EXPANDED_CONTAINER, NULL)); 166 } 167 168 void gtk_expanded_container_put(GtkExpandedContainer* container, 169 GtkWidget* widget, gint x, gint y) { 170 g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); 171 g_return_if_fail(GTK_IS_WIDGET(widget)); 172 gtk_fixed_put(GTK_FIXED(container), widget, x, y); 173 } 174 175 void gtk_expanded_container_move(GtkExpandedContainer* container, 176 GtkWidget* widget, gint x, gint y) { 177 g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); 178 g_return_if_fail(GTK_IS_WIDGET(widget)); 179 gtk_fixed_move(GTK_FIXED(container), widget, x, y); 180 } 181 182 void gtk_expanded_container_set_has_window(GtkExpandedContainer* container, 183 gboolean has_window) { 184 g_return_if_fail(GTK_IS_EXPANDED_CONTAINER(container)); 185 g_return_if_fail(!gtk_widget_get_realized(GTK_WIDGET(container))); 186 gtk_widget_set_has_window(GTK_WIDGET(container), has_window); 187 } 188 189 gboolean gtk_expanded_container_get_has_window( 190 GtkExpandedContainer* container) { 191 g_return_val_if_fail(GTK_IS_EXPANDED_CONTAINER(container), FALSE); 192 return gtk_widget_get_has_window(GTK_WIDGET(container)); 193 } 194 195 G_END_DECLS 196