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