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