Home | History | Annotate | Download | only in gfx
      1 // Copyright (c) 2012 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/gfx/gtk_preserve_window.h"
      6 
      7 #include <gdk/gdk.h>
      8 #include <gtk/gtk.h>
      9 
     10 #include "ui/base/gtk/gtk_compat.h"
     11 
     12 G_BEGIN_DECLS
     13 
     14 #define GTK_PRESERVE_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
     15                                             GTK_TYPE_PRESERVE_WINDOW, \
     16                                             GtkPreserveWindowPrivate))
     17 
     18 typedef struct _GtkPreserveWindowPrivate GtkPreserveWindowPrivate;
     19 
     20 struct _GtkPreserveWindowPrivate {
     21   // If true, don't create/destroy windows on realize/unrealize.
     22   gboolean preserve_window;
     23 
     24   // Whether or not we delegate the resize of the GdkWindow
     25   // to someone else.
     26   gboolean delegate_resize;
     27 
     28   // Accessible factory and userdata.
     29   AtkObject* (*accessible_factory)(void* userdata);
     30   void* accessible_factory_userdata;
     31 };
     32 
     33 G_DEFINE_TYPE(GtkPreserveWindow, gtk_preserve_window, GTK_TYPE_FIXED)
     34 
     35 static void gtk_preserve_window_destroy(GtkObject* object);
     36 static void gtk_preserve_window_realize(GtkWidget* widget);
     37 static void gtk_preserve_window_unrealize(GtkWidget* widget);
     38 static void gtk_preserve_window_size_allocate(GtkWidget* widget,
     39                                               GtkAllocation* allocation);
     40 static AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget);
     41 
     42 static void gtk_preserve_window_class_init(GtkPreserveWindowClass *klass) {
     43   GtkWidgetClass* widget_class = reinterpret_cast<GtkWidgetClass*>(klass);
     44   widget_class->realize = gtk_preserve_window_realize;
     45   widget_class->unrealize = gtk_preserve_window_unrealize;
     46   widget_class->size_allocate = gtk_preserve_window_size_allocate;
     47   widget_class->get_accessible = gtk_preserve_window_get_accessible;
     48 
     49   GtkObjectClass* object_class = reinterpret_cast<GtkObjectClass*>(klass);
     50   object_class->destroy = gtk_preserve_window_destroy;
     51 
     52   GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
     53   g_type_class_add_private(gobject_class, sizeof(GtkPreserveWindowPrivate));
     54 }
     55 
     56 static void gtk_preserve_window_init(GtkPreserveWindow* widget) {
     57   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
     58   priv->preserve_window = FALSE;
     59   priv->accessible_factory = NULL;
     60   priv->accessible_factory_userdata = NULL;
     61 
     62   // These widgets always have their own window.
     63   gtk_widget_set_has_window(GTK_WIDGET(widget), TRUE);
     64 }
     65 
     66 GtkWidget* gtk_preserve_window_new() {
     67   return GTK_WIDGET(g_object_new(GTK_TYPE_PRESERVE_WINDOW, NULL));
     68 }
     69 
     70 static void gtk_preserve_window_destroy(GtkObject* object) {
     71   GtkWidget* widget = reinterpret_cast<GtkWidget*>(object);
     72   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
     73 
     74   GdkWindow* gdk_window = gtk_widget_get_window(widget);
     75   if (gdk_window) {
     76     gdk_window_set_user_data(gdk_window, NULL);
     77     // If the window is preserved, someone else must destroy it.
     78     if (!priv->preserve_window)
     79       gdk_window_destroy(gdk_window);
     80     gtk_widget_set_window(widget, NULL);
     81   }
     82 
     83   GTK_OBJECT_CLASS(gtk_preserve_window_parent_class)->destroy(object);
     84 }
     85 
     86 static void gtk_preserve_window_realize(GtkWidget* widget) {
     87   g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
     88 
     89   GdkWindow* gdk_window = gtk_widget_get_window(widget);
     90   if (gdk_window) {
     91     GtkAllocation allocation;
     92     gtk_widget_get_allocation(widget, &allocation);
     93 
     94     gdk_window_reparent(gdk_window,
     95                         gtk_widget_get_parent_window(widget),
     96                         allocation.x,
     97                         allocation.y);
     98     GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
     99     if (!priv->delegate_resize) {
    100       gdk_window_resize(gdk_window,
    101                         allocation.width,
    102                         allocation.height);
    103     }
    104     gint event_mask = gtk_widget_get_events(widget);
    105     event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
    106     gdk_window_set_events(gdk_window, (GdkEventMask) event_mask);
    107     gdk_window_set_user_data(gdk_window, widget);
    108 
    109     gtk_widget_set_realized(widget, TRUE);
    110 
    111     gtk_widget_style_attach(widget);
    112     gtk_style_set_background(gtk_widget_get_style(widget),
    113                              gdk_window, GTK_STATE_NORMAL);
    114   } else {
    115     GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->realize(widget);
    116   }
    117 }
    118 
    119 static void gtk_preserve_window_unrealize(GtkWidget* widget) {
    120   g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
    121 
    122   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
    123   if (priv->preserve_window) {
    124     GtkWidgetClass* widget_class =
    125         GTK_WIDGET_CLASS(gtk_preserve_window_parent_class);
    126     GtkContainerClass* container_class =
    127         GTK_CONTAINER_CLASS(gtk_preserve_window_parent_class);
    128 
    129     if (gtk_widget_get_mapped(widget)) {
    130       widget_class->unmap(widget);
    131 
    132       gtk_widget_set_mapped(widget, FALSE);
    133     }
    134 
    135     // This is the behavior from GtkWidget, inherited by GtkFixed.
    136     // It is unclear why we should not call the potentially overridden
    137     // unrealize method (via the callback), but doing so causes errors.
    138     container_class->forall(
    139         GTK_CONTAINER(widget), FALSE,
    140         reinterpret_cast<GtkCallback>(gtk_widget_unrealize), NULL);
    141 
    142     GdkWindow* gdk_window = gtk_widget_get_window(widget);
    143 
    144     // TODO(erg): Almost all style handling will need to be overhauled in GTK3.
    145     gtk_style_detach(gtk_widget_get_style(widget));
    146     gdk_window_reparent(gdk_window, gdk_get_default_root_window(), 0, 0);
    147     gtk_selection_remove_all(widget);
    148     gdk_window_set_user_data(gdk_window, NULL);
    149 
    150     gtk_widget_set_realized(widget, FALSE);
    151   } else {
    152     GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)->unrealize(widget);
    153   }
    154 }
    155 
    156 gboolean gtk_preserve_window_get_preserve(GtkPreserveWindow* window) {
    157   g_return_val_if_fail(GTK_IS_PRESERVE_WINDOW(window), FALSE);
    158   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window);
    159 
    160   return priv->preserve_window;
    161 }
    162 
    163 void gtk_preserve_window_set_preserve(GtkPreserveWindow* window,
    164                                       gboolean value) {
    165   g_return_if_fail(GTK_IS_PRESERVE_WINDOW(window));
    166   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(window);
    167   priv->preserve_window = value;
    168 
    169   GtkWidget* widget = GTK_WIDGET(window);
    170   GdkWindow* gdk_window = gtk_widget_get_window(widget);
    171   if (value && !gdk_window) {
    172     GdkWindowAttr attributes;
    173     gint attributes_mask;
    174 
    175     // We may not know the width and height, so we rely on the fact
    176     // that a size-allocation will resize it later.
    177     attributes.width = 1;
    178     attributes.height = 1;
    179 
    180     attributes.window_type = GDK_WINDOW_CHILD;
    181     attributes.wclass = GDK_INPUT_OUTPUT;
    182     attributes.override_redirect = TRUE;
    183 
    184     attributes.visual = gtk_widget_get_visual(widget);
    185     attributes.colormap = gtk_widget_get_colormap(widget);
    186 
    187     attributes.event_mask = gtk_widget_get_events(widget);
    188     attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK;
    189 
    190     attributes_mask = GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_NOREDIR;
    191     gdk_window = gdk_window_new(
    192         gdk_get_default_root_window(), &attributes, attributes_mask);
    193     gtk_widget_set_window(widget, gdk_window);
    194   } else if (!value && gdk_window && !gtk_widget_get_realized(widget)) {
    195     gdk_window_destroy(gdk_window);
    196     gtk_widget_set_window(widget, NULL);
    197   }
    198 }
    199 
    200 void gtk_preserve_window_size_allocate(GtkWidget* widget,
    201                                        GtkAllocation* allocation) {
    202   g_return_if_fail(GTK_IS_PRESERVE_WINDOW(widget));
    203 
    204   gtk_widget_set_allocation(widget, allocation);
    205 
    206   if (gtk_widget_get_realized(widget)) {
    207     GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
    208     GdkWindow* gdk_window = gtk_widget_get_window(widget);
    209     if (priv->delegate_resize) {
    210       gdk_window_move(gdk_window, allocation->x, allocation->y);
    211     } else {
    212       gdk_window_move_resize(
    213           gdk_window, allocation->x, allocation->y,
    214           allocation->width, allocation->height);
    215     }
    216   }
    217 
    218   // Propagate resize to children
    219   guint16 border_width = gtk_container_get_border_width(GTK_CONTAINER(widget));
    220   GList *children = GTK_FIXED(widget)->children;
    221   while (children) {
    222     GtkFixedChild *child = reinterpret_cast<GtkFixedChild*>(children->data);
    223     if (gtk_widget_get_visible(child->widget)) {
    224       GtkRequisition child_requisition;
    225       gtk_widget_get_child_requisition(child->widget, &child_requisition);
    226 
    227       GtkAllocation child_allocation;
    228       child_allocation.x = child->x + border_width;
    229       child_allocation.y = child->y + border_width;
    230       child_allocation.width = child_requisition.width;
    231       child_allocation.height = child_requisition.height;
    232 
    233       gtk_widget_size_allocate(child->widget, &child_allocation);
    234     }
    235     children = children->next;
    236   }
    237 }
    238 
    239 void gtk_preserve_window_delegate_resize(GtkPreserveWindow* widget,
    240                                          gboolean delegate) {
    241   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
    242   priv->delegate_resize = delegate;
    243 }
    244 
    245 void gtk_preserve_window_set_accessible_factory(
    246     GtkPreserveWindow* widget,
    247     AtkObject* (*factory)(void* userdata),
    248     gpointer userdata) {
    249   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
    250   priv->accessible_factory = factory;
    251   priv->accessible_factory_userdata = userdata;
    252 }
    253 
    254 AtkObject* gtk_preserve_window_get_accessible(GtkWidget* widget) {
    255   GtkPreserveWindowPrivate* priv = GTK_PRESERVE_WINDOW_GET_PRIVATE(widget);
    256   if (priv->accessible_factory) {
    257     return priv->accessible_factory(priv->accessible_factory_userdata);
    258   } else {
    259     return GTK_WIDGET_CLASS(gtk_preserve_window_parent_class)
    260         ->get_accessible(widget);
    261   }
    262 }
    263 
    264 G_END_DECLS
    265