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
13namespace {
14
15enum {
16  CHILD_SIZE_REQUEST,
17  LAST_SIGNAL
18};
19
20guint expanded_container_signals[LAST_SIGNAL] = { 0 };
21
22struct SizeAllocateData {
23  GtkWidget* container;
24  GtkAllocation* allocation;
25  int border_width;
26};
27
28void 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
38void 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
78void 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
113G_BEGIN_DECLS
114
115static void gtk_expanded_container_size_allocate(GtkWidget* widget,
116                                                 GtkAllocation* allocation);
117
118G_DEFINE_TYPE(GtkExpandedContainer, gtk_expanded_container, GTK_TYPE_FIXED)
119
120static 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
141static void gtk_expanded_container_init(GtkExpandedContainer* container) {
142}
143
144static 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
164GtkWidget* gtk_expanded_container_new() {
165  return GTK_WIDGET(g_object_new(GTK_TYPE_EXPANDED_CONTAINER, NULL));
166}
167
168void 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
175void 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
182void 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
189gboolean 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
195G_END_DECLS
196