Home | History | Annotate | Download | only in gtk
      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/base/gtk/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