// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/widget/widget_gtk.h"

#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <X11/extensions/shape.h>

#include <set>
#include <vector>

#include "app/drag_drop_types.h"
#include "app/os_exchange_data.h"
#include "app/os_exchange_data_provider_gtk.h"
#include "base/auto_reset.h"
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "gfx/path.h"
#include "views/widget/default_theme_provider.h"
#include "views/widget/drop_target_gtk.h"
#include "views/widget/gtk_views_fixed.h"
#include "views/widget/root_view.h"
#include "views/widget/tooltip_manager_gtk.h"
#include "views/widget/widget_delegate.h"
#include "views/window/window_gtk.h"

namespace {

// g_object data keys to associate a WidgetGtk object to a GtkWidget.
const char* kWidgetKey = "__VIEWS_WIDGET__";
const wchar_t* kWidgetWideKey = L"__VIEWS_WIDGET__";
// A g_object data key to associate a CompositePainter object to a GtkWidget.
const char* kCompositePainterKey = "__VIEWS_COMPOSITE_PAINTER__";
// A g_object data key to associate the flag whether or not the widget
// is composited to a GtkWidget. gtk_widget_is_composited simply tells
// if x11 supports composition and cannot be used to tell if given widget
// is composited.
const char* kCompositeEnabledKey = "__VIEWS_COMPOSITE_ENABLED__";

// CompositePainter draws a composited child widgets image into its
// drawing area. This object is created at most once for a widget and kept
// until the widget is destroyed.
class CompositePainter {
 public:
  explicit CompositePainter(GtkWidget* parent)
      : parent_object_(G_OBJECT(parent)) {
    handler_id_ = g_signal_connect_after(
        parent_object_, "expose_event", G_CALLBACK(OnCompositePaint), NULL);
  }

  static void AddCompositePainter(GtkWidget* widget) {
    CompositePainter* painter = static_cast<CompositePainter*>(
        g_object_get_data(G_OBJECT(widget), kCompositePainterKey));
    if (!painter) {
      g_object_set_data(G_OBJECT(widget), kCompositePainterKey,
                        new CompositePainter(widget));
      g_signal_connect(widget, "destroy",
                       G_CALLBACK(&DestroyPainter), NULL);
    }
  }

  // Enable the composition.
  static void SetComposited(GtkWidget* widget) {
    DCHECK(GTK_WIDGET_REALIZED(widget));
    gdk_window_set_composited(widget->window, true);
    g_object_set_data(G_OBJECT(widget), kCompositeEnabledKey,
                      const_cast<char*>(""));
  }

  // Returns true if the |widget| is composited.
  static bool IsComposited(GtkWidget* widget) {
    return g_object_get_data(G_OBJECT(widget), kCompositeEnabledKey) != NULL;
  }

 private:
  virtual ~CompositePainter() {}

  // Composes a image from one child.
  static void CompositeChildWidget(GtkWidget* child, gpointer data) {
    GdkEventExpose* event = static_cast<GdkEventExpose*>(data);
    GtkWidget* parent = gtk_widget_get_parent(child);
    DCHECK(parent);
    if (IsComposited(child)) {
      cairo_t* cr = gdk_cairo_create(parent->window);
      gdk_cairo_set_source_pixmap(cr, child->window,
                                  child->allocation.x,
                                  child->allocation.y);
      GdkRegion* region = gdk_region_rectangle(&child->allocation);
      gdk_region_intersect(region, event->region);
      gdk_cairo_region(cr, region);
      cairo_clip(cr);
      cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
      cairo_paint(cr);
      cairo_destroy(cr);
    }
  }

  // Expose-event handler that compose & draws children's image into
  // the |parent|'s drawing area.
  static gboolean OnCompositePaint(GtkWidget* parent, GdkEventExpose* event) {
    gtk_container_foreach(GTK_CONTAINER(parent),
                          CompositeChildWidget,
                          event);
    return false;
  }

  static void DestroyPainter(GtkWidget* object) {
    CompositePainter* painter = reinterpret_cast<CompositePainter*>(
        g_object_get_data(G_OBJECT(object), kCompositePainterKey));
    DCHECK(painter);
    delete painter;
  }

  GObject* parent_object_;
  gulong handler_id_;

  DISALLOW_COPY_AND_ASSIGN(CompositePainter);
};

}  // namespace

namespace views {

// During drag and drop GTK sends a drag-leave during a drop. This means we
// have no way to tell the difference between a normal drag leave and a drop.
// To work around that we listen for DROP_START, then ignore the subsequent
// drag-leave that GTK generates.
class WidgetGtk::DropObserver : public MessageLoopForUI::Observer {
 public:
  DropObserver() {}

  static DropObserver* Get() {
    return Singleton<DropObserver>::get();
  }

  virtual void WillProcessEvent(GdkEvent* event) {
    if (event->type == GDK_DROP_START) {
      WidgetGtk* widget = GetWidgetGtkForEvent(event);
      if (widget)
        widget->ignore_drag_leave_ = true;
    }
  }

  virtual void DidProcessEvent(GdkEvent* event) {
  }

 private:
  WidgetGtk* GetWidgetGtkForEvent(GdkEvent* event) {
    GtkWidget* gtk_widget = gtk_get_event_widget(event);
    if (!gtk_widget)
      return NULL;

    return WidgetGtk::GetViewForNative(gtk_widget);
  }

  DISALLOW_COPY_AND_ASSIGN(DropObserver);
};

// Returns the position of a widget on screen.
static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) {
  // First get the root window.
  GtkWidget* root = widget;
  while (root && !GTK_IS_WINDOW(root)) {
    root = gtk_widget_get_parent(root);
  }
  if (!root) {
    // If root is null we're not parented. Return 0x0 and assume the caller will
    // query again when we're parented.
    *x = *y = 0;
    return;
  }
  // Translate the coordinate from widget to root window.
  gtk_widget_translate_coordinates(widget, root, 0, 0, x, y);
  // Then adjust the position with the position of the root window.
  int window_x, window_y;
  gtk_window_get_position(GTK_WINDOW(root), &window_x, &window_y);
  *x += window_x;
  *y += window_y;
}

// "expose-event" handler of drag icon widget that renders drag image pixbuf.
static gboolean DragIconWidgetPaint(GtkWidget* widget,
                                    GdkEventExpose* event,
                                    gpointer data) {
  GdkPixbuf* pixbuf = reinterpret_cast<GdkPixbuf*>(data);

  cairo_t* cr = gdk_cairo_create(widget->window);

  gdk_cairo_region(cr, event->region);
  cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
  gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0);
  cairo_paint(cr);

  cairo_destroy(cr);
  return true;
}

// Creates a drag icon widget that draws drag_image.
static GtkWidget* CreateDragIconWidget(GdkPixbuf* drag_image) {
  GdkColormap* rgba_colormap =
      gdk_screen_get_rgba_colormap(gdk_screen_get_default());
  if (!rgba_colormap)
    return NULL;

  GtkWidget* drag_widget = gtk_window_new(GTK_WINDOW_POPUP);

  gtk_widget_set_colormap(drag_widget, rgba_colormap);
  gtk_widget_set_app_paintable(drag_widget, true);
  gtk_widget_set_size_request(drag_widget,
                              gdk_pixbuf_get_width(drag_image),
                              gdk_pixbuf_get_height(drag_image));

  g_signal_connect(G_OBJECT(drag_widget), "expose-event",
                   G_CALLBACK(&DragIconWidgetPaint), drag_image);
  return drag_widget;
}

// static
GtkWidget* WidgetGtk::null_parent_ = NULL;

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, public:

WidgetGtk::WidgetGtk(Type type)
    : is_window_(false),
      type_(type),
      widget_(NULL),
      window_contents_(NULL),
      focus_manager_(NULL),
      is_mouse_down_(false),
      has_capture_(false),
      last_mouse_event_was_move_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
      delete_on_destroy_(true),
      transparent_(false),
      ignore_events_(false),
      ignore_drag_leave_(false),
      opacity_(255),
      drag_data_(NULL),
      in_paint_now_(false),
      is_active_(false),
      transient_to_parent_(false),
      got_initial_focus_in_(false),
      has_focus_(false),
      delegate_(NULL),
      always_on_top_(false),
      is_double_buffered_(false) {
  static bool installed_message_loop_observer = false;
  if (!installed_message_loop_observer) {
    installed_message_loop_observer = true;
    MessageLoopForUI* loop = MessageLoopForUI::current();
    if (loop)
      loop->AddObserver(DropObserver::Get());
  }

  if (type_ != TYPE_CHILD)
    focus_manager_ = new FocusManager(this);
}

WidgetGtk::~WidgetGtk() {
  DCHECK(delete_on_destroy_ || widget_ == NULL);
  if (type_ != TYPE_CHILD)
    ActiveWindowWatcherX::RemoveObserver(this);

  // Defer focus manager's destruction. This is for the case when the
  // focus manager is referenced by a child WidgetGtk (e.g. TabbedPane in a
  // dialog). When gtk_widget_destroy is called on the parent, the destroy
  // signal reaches parent first and then the child. Thus causing the parent
  // WidgetGtk's dtor executed before the child's. If child's view hierarchy
  // references this focus manager, it crashes. This will defer focus manager's
  // destruction after child WidgetGtk's dtor.
  if (focus_manager_)
    MessageLoop::current()->DeleteSoon(FROM_HERE, focus_manager_);
}

GtkWindow* WidgetGtk::GetTransientParent() const {
  return (type_ != TYPE_CHILD && widget_) ?
      gtk_window_get_transient_for(GTK_WINDOW(widget_)) : NULL;
}

bool WidgetGtk::MakeTransparent() {
  // Transparency can only be enabled only if we haven't realized the widget.
  DCHECK(!widget_);

  if (!gdk_screen_is_composited(gdk_screen_get_default())) {
    // Transparency is only supported for compositing window managers.
    // NOTE: there's a race during ChromeOS startup such that X might think
    // compositing isn't supported. We ignore it if the wm says compositing
    // isn't supported.
    DLOG(WARNING) << "compositing not supported; allowing anyway";
  }

  if (!gdk_screen_get_rgba_colormap(gdk_screen_get_default())) {
    // We need rgba to make the window transparent.
    return false;
  }

  transparent_ = true;
  return true;
}

void WidgetGtk::EnableDoubleBuffer(bool enabled) {
  is_double_buffered_ = enabled;
  if (window_contents_) {
    if (is_double_buffered_)
      GTK_WIDGET_SET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
    else
      GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
  }
}

bool WidgetGtk::MakeIgnoreEvents() {
  // Transparency can only be enabled for windows/popups and only if we haven't
  // realized the widget.
  DCHECK(!widget_ && type_ != TYPE_CHILD);

  ignore_events_ = true;
  return true;
}

void WidgetGtk::AddChild(GtkWidget* child) {
  gtk_container_add(GTK_CONTAINER(window_contents_), child);
}

void WidgetGtk::RemoveChild(GtkWidget* child) {
  // We can be called after the contents widget has been destroyed, e.g. any
  // NativeViewHost not removed from the view hierarchy before the window is
  // closed.
  if (GTK_IS_CONTAINER(window_contents_)) {
    gtk_container_remove(GTK_CONTAINER(window_contents_), child);
    gtk_views_fixed_set_use_allocated_size(child, false);
  }
}

void WidgetGtk::ReparentChild(GtkWidget* child) {
  gtk_widget_reparent(child, window_contents_);
}

void WidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, int h) {
  GtkAllocation alloc = { x, y, w, h };
  gtk_widget_size_allocate(child, &alloc);
  gtk_views_fixed_set_use_allocated_size(child, true);
  gtk_fixed_move(GTK_FIXED(window_contents_), child, x, y);
}

void WidgetGtk::DoDrag(const OSExchangeData& data, int operation) {
  const OSExchangeDataProviderGtk& data_provider =
      static_cast<const OSExchangeDataProviderGtk&>(data.provider());
  GtkTargetList* targets = data_provider.GetTargetList();
  GdkEvent* current_event = gtk_get_current_event();
  const OSExchangeDataProviderGtk& provider(
      static_cast<const OSExchangeDataProviderGtk&>(data.provider()));

  GdkDragContext* context = gtk_drag_begin(
      window_contents_,
      targets,
      static_cast<GdkDragAction>(
          DragDropTypes::DragOperationToGdkDragAction(operation)),
      1,
      current_event);

  GtkWidget* drag_icon_widget = NULL;

  // Set the drag image if one was supplied.
  if (provider.drag_image()) {
    drag_icon_widget = CreateDragIconWidget(provider.drag_image());
    if (drag_icon_widget) {
      // Use a widget as the drag icon when compositing is enabled for proper
      // transparency handling.
      g_object_ref(provider.drag_image());
      gtk_drag_set_icon_widget(context,
                               drag_icon_widget,
                               provider.cursor_offset().x(),
                               provider.cursor_offset().y());
    } else {
      gtk_drag_set_icon_pixbuf(context,
                               provider.drag_image(),
                               provider.cursor_offset().x(),
                               provider.cursor_offset().y());
    }
  }

  if (current_event)
    gdk_event_free(current_event);
  gtk_target_list_unref(targets);

  drag_data_ = &data_provider;

  // Block the caller until drag is done by running a nested message loop.
  MessageLoopForUI::current()->Run(NULL);

  drag_data_ = NULL;

  if (drag_icon_widget) {
    gtk_widget_destroy(drag_icon_widget);
    g_object_unref(provider.drag_image());
  }
}

void WidgetGtk::SetFocusTraversableParent(FocusTraversable* parent) {
  root_view_->SetFocusTraversableParent(parent);
}

void WidgetGtk::SetFocusTraversableParentView(View* parent_view) {
  root_view_->SetFocusTraversableParentView(parent_view);
}

void WidgetGtk::IsActiveChanged() {
  if (GetWidgetDelegate())
    GetWidgetDelegate()->IsActiveChanged(IsActive());
}

// static
WidgetGtk* WidgetGtk::GetViewForNative(GtkWidget* widget) {
  return static_cast<WidgetGtk*>(GetWidgetFromNativeView(widget));
}

void WidgetGtk::ResetDropTarget() {
  ignore_drag_leave_ = false;
  drop_target_.reset(NULL);
}

// static
RootView* WidgetGtk::GetRootViewForWidget(GtkWidget* widget) {
  gpointer user_data = g_object_get_data(G_OBJECT(widget), "root-view");
  return static_cast<RootView*>(user_data);
}

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, ActiveWindowWatcherX::Observer implementation:

void WidgetGtk::ActiveWindowChanged(GdkWindow* active_window) {
  if (!GetNativeView())
    return;

  bool was_active = IsActive();
  is_active_ = (active_window == GTK_WIDGET(GetNativeView())->window);
  if (!is_active_ && active_window && type_ != TYPE_CHILD) {
    // We're not active, but the force the window to be rendered as active if
    // a child window is transient to us.
    gpointer data = NULL;
    gdk_window_get_user_data(active_window, &data);
    GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
    is_active_ =
        (widget && GTK_IS_WINDOW(widget) &&
         gtk_window_get_transient_for(GTK_WINDOW(widget)) == GTK_WINDOW(
             widget_));
  }
  if (was_active != IsActive())
    IsActiveChanged();
}

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, Widget implementation:

void WidgetGtk::InitWithWidget(Widget* parent,
                               const gfx::Rect& bounds) {
  WidgetGtk* parent_gtk = static_cast<WidgetGtk*>(parent);
  GtkWidget* native_parent = NULL;
  if (parent != NULL) {
    if (type_ != TYPE_CHILD) {
      // window's parent has to be window.
      native_parent = parent_gtk->GetNativeView();
    } else {
      native_parent = parent_gtk->window_contents();
    }
  }
  Init(native_parent, bounds);
}

void WidgetGtk::Init(GtkWidget* parent,
                     const gfx::Rect& bounds) {
  if (type_ != TYPE_CHILD)
    ActiveWindowWatcherX::AddObserver(this);
  // Force creation of the RootView if it hasn't been created yet.
  GetRootView();

  default_theme_provider_.reset(new DefaultThemeProvider());

  // Make container here.
  CreateGtkWidget(parent, bounds);

  if (opacity_ != 255)
    SetOpacity(opacity_);

  // Make sure we receive our motion events.

  // In general we register most events on the parent of all widgets. At a
  // minimum we need painting to happen on the parent (otherwise painting
  // doesn't work at all), and similarly we need mouse release events on the
  // parent as windows don't get mouse releases.
  gtk_widget_add_events(window_contents_,
                        GDK_ENTER_NOTIFY_MASK |
                        GDK_LEAVE_NOTIFY_MASK |
                        GDK_BUTTON_PRESS_MASK |
                        GDK_BUTTON_RELEASE_MASK |
                        GDK_POINTER_MOTION_MASK |
                        GDK_KEY_PRESS_MASK |
                        GDK_KEY_RELEASE_MASK);
  SetRootViewForWidget(widget_, root_view_.get());

  g_signal_connect_after(G_OBJECT(window_contents_), "size_allocate",
                         G_CALLBACK(&OnSizeAllocateThunk), this);
  gtk_widget_set_app_paintable(window_contents_, true);
  g_signal_connect(window_contents_, "expose_event",
                   G_CALLBACK(&OnPaintThunk), this);
  g_signal_connect(window_contents_, "enter_notify_event",
                   G_CALLBACK(&OnEnterNotifyThunk), this);
  g_signal_connect(window_contents_, "leave_notify_event",
                   G_CALLBACK(&OnLeaveNotifyThunk), this);
  g_signal_connect(window_contents_, "motion_notify_event",
                   G_CALLBACK(&OnMotionNotifyThunk), this);
  g_signal_connect(window_contents_, "button_press_event",
                   G_CALLBACK(&OnButtonPressThunk), this);
  g_signal_connect(window_contents_, "button_release_event",
                   G_CALLBACK(&OnButtonReleaseThunk), this);
  g_signal_connect(window_contents_, "grab_broken_event",
                   G_CALLBACK(&OnGrabBrokeEventThunk), this);
  g_signal_connect(window_contents_, "grab_notify",
                   G_CALLBACK(&OnGrabNotifyThunk), this);
  g_signal_connect(window_contents_, "scroll_event",
                   G_CALLBACK(&OnScrollThunk), this);
  g_signal_connect(window_contents_, "visibility_notify_event",
                   G_CALLBACK(&OnVisibilityNotifyThunk), this);

  // In order to receive notification when the window is no longer the front
  // window, we need to install these on the widget.
  // NOTE: this doesn't work with focus follows mouse.
  g_signal_connect(widget_, "focus_in_event",
                   G_CALLBACK(&OnFocusInThunk), this);
  g_signal_connect(widget_, "focus_out_event",
                   G_CALLBACK(&OnFocusOutThunk), this);
  g_signal_connect(widget_, "destroy",
                   G_CALLBACK(&OnDestroyThunk), this);
  g_signal_connect(widget_, "show",
                   G_CALLBACK(&OnShowThunk), this);
  g_signal_connect(widget_, "hide",
                   G_CALLBACK(&OnHideThunk), this);

  // Views/FocusManager (re)sets the focus to the root window,
  // so we need to connect signal handlers to the gtk window.
  // See views::Views::Focus and views::FocusManager::ClearNativeFocus
  // for more details.
  g_signal_connect(widget_, "key_press_event",
                   G_CALLBACK(&OnKeyPressThunk), this);
  g_signal_connect(widget_, "key_release_event",
                   G_CALLBACK(&OnKeyReleaseThunk), this);

  // Drag and drop.
  gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0),
                    NULL, 0, GDK_ACTION_COPY);
  g_signal_connect(window_contents_, "drag_motion",
                   G_CALLBACK(&OnDragMotionThunk), this);
  g_signal_connect(window_contents_, "drag_data_received",
                   G_CALLBACK(&OnDragDataReceivedThunk), this);
  g_signal_connect(window_contents_, "drag_drop",
                   G_CALLBACK(&OnDragDropThunk), this);
  g_signal_connect(window_contents_, "drag_leave",
                   G_CALLBACK(&OnDragLeaveThunk), this);
  g_signal_connect(window_contents_, "drag_data_get",
                   G_CALLBACK(&OnDragDataGetThunk), this);
  g_signal_connect(window_contents_, "drag_end",
                   G_CALLBACK(&OnDragEndThunk), this);
  g_signal_connect(window_contents_, "drag_failed",
                   G_CALLBACK(&OnDragFailedThunk), this);

  tooltip_manager_.reset(new TooltipManagerGtk(this));

  // Register for tooltips.
  g_object_set(G_OBJECT(window_contents_), "has-tooltip", TRUE, NULL);
  g_signal_connect(window_contents_, "query_tooltip",
                   G_CALLBACK(&OnQueryTooltipThunk), this);

  if (type_ == TYPE_CHILD) {
    if (parent) {
      SetBounds(bounds);
    }
  } else {
    if (bounds.width() > 0 && bounds.height() > 0)
      gtk_window_resize(GTK_WINDOW(widget_), bounds.width(), bounds.height());
    gtk_window_move(GTK_WINDOW(widget_), bounds.x(), bounds.y());
  }
}

WidgetDelegate* WidgetGtk::GetWidgetDelegate() {
  return delegate_;
}

void WidgetGtk::SetWidgetDelegate(WidgetDelegate* delegate) {
  delegate_ = delegate;
}

void WidgetGtk::SetContentsView(View* view) {
  root_view_->SetContentsView(view);
}

void WidgetGtk::GetBounds(gfx::Rect* out, bool including_frame) const {
  if (!widget_) {
    // Due to timing we can get a request for the bounds after Close.
    *out = gfx::Rect(gfx::Point(0, 0), size_);
    return;
  }

  int x = 0, y = 0, w, h;
  if (GTK_IS_WINDOW(widget_)) {
    gtk_window_get_position(GTK_WINDOW(widget_), &x, &y);
    // NOTE: this doesn't include frame decorations, but it should be good
    // enough for our uses.
    gtk_window_get_size(GTK_WINDOW(widget_), &w, &h);
  } else {
    GetWidgetPositionOnScreen(widget_, &x, &y);
    w = widget_->allocation.width;
    h = widget_->allocation.height;
  }

  return out->SetRect(x, y, w, h);
}

void WidgetGtk::SetBounds(const gfx::Rect& bounds) {
  if (type_ == TYPE_CHILD) {
    GtkWidget* parent = gtk_widget_get_parent(widget_);
    if (GTK_IS_VIEWS_FIXED(parent)) {
      WidgetGtk* parent_widget = GetViewForNative(parent);
      parent_widget->PositionChild(widget_, bounds.x(), bounds.y(),
                                   bounds.width(), bounds.height());
    } else {
      DCHECK(GTK_IS_FIXED(parent))
          << "Parent of WidgetGtk has to be Fixed or ViewsFixed";
      // Just request the size if the parent is not WidgetGtk but plain
      // GtkFixed. WidgetGtk does not know the minimum size so we assume
      // the caller of the SetBounds knows exactly how big it wants to be.
      gtk_widget_set_size_request(widget_, bounds.width(), bounds.height());
      if (parent != null_parent_)
        gtk_fixed_move(GTK_FIXED(parent), widget_, bounds.x(), bounds.y());
    }
  } else {
    if (GTK_WIDGET_MAPPED(widget_)) {
      // If the widget is mapped (on screen), we can move and resize with one
      // call, which avoids two separate window manager steps.
      gdk_window_move_resize(widget_->window, bounds.x(), bounds.y(),
                             bounds.width(), bounds.height());
    }

    // Always call gtk_window_move and gtk_window_resize so that GtkWindow's
    // geometry info is up-to-date.
    GtkWindow* gtk_window = GTK_WINDOW(widget_);
    // TODO: this may need to set an initial size if not showing.
    // TODO: need to constrain based on screen size.
    if (!bounds.IsEmpty()) {
      gtk_window_resize(gtk_window, bounds.width(), bounds.height());
    }
    gtk_window_move(gtk_window, bounds.x(), bounds.y());
  }
}

void WidgetGtk::MoveAbove(Widget* widget) {
  DCHECK(widget_);
  DCHECK(widget_->window);
  // TODO(oshima): gdk_window_restack is not available in gtk2.0, so
  // we're simply raising the window to the top. We should switch to
  // gdk_window_restack when we upgrade gtk to 2.18 or up.
  gdk_window_raise(widget_->window);
}

void WidgetGtk::SetShape(gfx::NativeRegion region) {
  DCHECK(widget_);
  DCHECK(widget_->window);
  gdk_window_shape_combine_region(widget_->window, region, 0, 0);
  gdk_region_destroy(region);
}

void WidgetGtk::Close() {
  if (!widget_)
    return;  // No need to do anything.

  // Hide first.
  Hide();
  if (close_widget_factory_.empty()) {
    // And we delay the close just in case we're on the stack.
    MessageLoop::current()->PostTask(FROM_HERE,
        close_widget_factory_.NewRunnableMethod(
            &WidgetGtk::CloseNow));
  }
}

void WidgetGtk::CloseNow() {
  if (widget_) {
    gtk_widget_destroy(widget_);
    widget_ = NULL;
  }
}

void WidgetGtk::Show() {
  if (widget_) {
    gtk_widget_show(widget_);
    if (widget_->window)
      gdk_window_raise(widget_->window);
  }
}

void WidgetGtk::Hide() {
  if (widget_) {
    gtk_widget_hide(widget_);
    if (widget_->window)
      gdk_window_lower(widget_->window);
  }
}

gfx::NativeView WidgetGtk::GetNativeView() const {
  return widget_;
}

void WidgetGtk::PaintNow(const gfx::Rect& update_rect) {
  if (widget_ && GTK_WIDGET_DRAWABLE(widget_)) {
    gtk_widget_queue_draw_area(widget_, update_rect.x(), update_rect.y(),
                               update_rect.width(), update_rect.height());
    // Force the paint to occur now.
    AutoReset<bool> auto_reset_in_paint_now(&in_paint_now_, true);
    gdk_window_process_updates(widget_->window, true);
  }
}

void WidgetGtk::SetOpacity(unsigned char opacity) {
  opacity_ = opacity;
  if (widget_) {
    // We can only set the opacity when the widget has been realized.
    gdk_window_set_opacity(widget_->window, static_cast<gdouble>(opacity) /
                           static_cast<gdouble>(255));
  }
}

void WidgetGtk::SetAlwaysOnTop(bool on_top) {
  DCHECK(type_ != TYPE_CHILD);
  always_on_top_ = on_top;
  if (widget_)
    gtk_window_set_keep_above(GTK_WINDOW(widget_), on_top);
}

RootView* WidgetGtk::GetRootView() {
  if (!root_view_.get()) {
    // First time the root view is being asked for, create it now.
    root_view_.reset(CreateRootView());
  }
  return root_view_.get();
}

Widget* WidgetGtk::GetRootWidget() const {
  GtkWidget* parent = widget_;
  GtkWidget* last_parent = parent;
  while (parent) {
    last_parent = parent;
    parent = gtk_widget_get_parent(parent);
  }
  return last_parent ? GetViewForNative(last_parent) : NULL;
}

bool WidgetGtk::IsVisible() const {
  return GTK_WIDGET_VISIBLE(widget_);
}

bool WidgetGtk::IsActive() const {
  DCHECK(type_ != TYPE_CHILD);
  return is_active_;
}

void WidgetGtk::GenerateMousePressedForView(View* view,
                                            const gfx::Point& point) {
  NOTIMPLEMENTED();
}

TooltipManager* WidgetGtk::GetTooltipManager() {
  return tooltip_manager_.get();
}

bool WidgetGtk::GetAccelerator(int cmd_id, menus::Accelerator* accelerator) {
  NOTIMPLEMENTED();
  return false;
}

Window* WidgetGtk::GetWindow() {
  return GetWindowImpl(widget_);
}

const Window* WidgetGtk::GetWindow() const {
  return GetWindowImpl(widget_);
}

void WidgetGtk::SetNativeWindowProperty(const std::wstring& name,
                                        void* value) {
  g_object_set_data(G_OBJECT(widget_), WideToUTF8(name).c_str(), value);
}

void* WidgetGtk::GetNativeWindowProperty(const std::wstring& name) {
  return g_object_get_data(G_OBJECT(widget_), WideToUTF8(name).c_str());
}

ThemeProvider* WidgetGtk::GetThemeProvider() const {
  return default_theme_provider_.get();
}

ThemeProvider* WidgetGtk::GetDefaultThemeProvider() const {
  return default_theme_provider_.get();
}

FocusManager* WidgetGtk::GetFocusManager() {
  if (focus_manager_)
    return focus_manager_;

  Widget* root = GetRootWidget();
  if (root && root != this) {
    // Widget subclasses may override GetFocusManager(), for example for
    // dealing with cases where the widget has been unparented.
    return root->GetFocusManager();
  }
  return NULL;
}

void WidgetGtk::ViewHierarchyChanged(bool is_add, View *parent,
                                     View *child) {
  if (drop_target_.get())
    drop_target_->ResetTargetViewIfEquals(child);
}

bool WidgetGtk::ContainsNativeView(gfx::NativeView native_view) {
  // TODO(port)  See implementation in WidgetWin::ContainsNativeView.
  NOTREACHED() << "WidgetGtk::ContainsNativeView is not implemented.";
  return false;
}

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, FocusTraversable implementation:

View* WidgetGtk::FindNextFocusableView(
    View* starting_view, bool reverse, Direction direction,
    bool check_starting_view, FocusTraversable** focus_traversable,
    View** focus_traversable_view) {
  return root_view_->FindNextFocusableView(starting_view,
                                           reverse,
                                           direction,
                                           check_starting_view,
                                           focus_traversable,
                                           focus_traversable_view);
}

FocusTraversable* WidgetGtk::GetFocusTraversableParent() {
  // We are a proxy to the root view, so we should be bypassed when traversing
  // up and as a result this should not be called.
  NOTREACHED();
  return NULL;
}

View* WidgetGtk::GetFocusTraversableParentView() {
  // We are a proxy to the root view, so we should be bypassed when traversing
  // up and as a result this should not be called.
  NOTREACHED();
  return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, protected:

// static
int WidgetGtk::GetFlagsForEventButton(const GdkEventButton& event) {
  int flags = Event::GetFlagsFromGdkState(event.state);
  switch (event.button) {
    case 1:
      flags |= Event::EF_LEFT_BUTTON_DOWN;
      break;
    case 2:
      flags |= Event::EF_MIDDLE_BUTTON_DOWN;
      break;
    case 3:
      flags |= Event::EF_RIGHT_BUTTON_DOWN;
      break;
    default:
      // We only deal with 1-3.
      break;
  }
  if (event.type == GDK_2BUTTON_PRESS)
    flags |= MouseEvent::EF_IS_DOUBLE_CLICK;
  return flags;
}

void WidgetGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
  // See comment next to size_ as to why we do this. Also note, it's tempting
  // to put this in the static method so subclasses don't need to worry about
  // it, but if a subclasses needs to set a shape then they need to always
  // reset the shape in this method regardless of whether the size changed.
  gfx::Size new_size(allocation->width, allocation->height);
  if (new_size == size_)
    return;
  size_ = new_size;
  root_view_->SetBounds(0, 0, allocation->width, allocation->height);
  root_view_->SchedulePaint();
}

gboolean WidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) {
  if (transparent_) {
    // Clear the background before drawing any view and native components.
    DrawTransparentBackground(widget, event);
  }
  root_view_->OnPaint(event);
  return false;  // False indicates other widgets should get the event as well.
}

void WidgetGtk::OnDragDataGet(GtkWidget* widget,
                              GdkDragContext* context,
                              GtkSelectionData* data,
                              guint info,
                              guint time) {
  if (!drag_data_) {
    NOTREACHED();
    return;
  }
  drag_data_->WriteFormatToSelection(info, data);
}

void WidgetGtk::OnDragDataReceived(GtkWidget* widget,
                                   GdkDragContext* context,
                                   gint x,
                                   gint y,
                                   GtkSelectionData* data,
                                   guint info,
                                   guint time) {
  if (drop_target_.get())
    drop_target_->OnDragDataReceived(context, x, y, data, info, time);
}

gboolean WidgetGtk::OnDragDrop(GtkWidget* widget,
                               GdkDragContext* context,
                               gint x,
                               gint y,
                               guint time) {
  if (drop_target_.get()) {
    return drop_target_->OnDragDrop(context, x, y, time);
  }
  return FALSE;
}

void WidgetGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context) {
  if (!drag_data_) {
    // This indicates we didn't start a drag operation, and should never
    // happen.
    NOTREACHED();
    return;
  }
  // Quit the nested message loop we spawned in DoDrag.
  MessageLoop::current()->Quit();
}

gboolean WidgetGtk::OnDragFailed(GtkWidget* widget,
                                 GdkDragContext* context,
                                 GtkDragResult result) {
  return FALSE;
}

void WidgetGtk::OnDragLeave(GtkWidget* widget,
                            GdkDragContext* context,
                            guint time) {
  if (ignore_drag_leave_) {
    ignore_drag_leave_ = false;
    return;
  }
  if (drop_target_.get()) {
    drop_target_->OnDragLeave(context, time);
    drop_target_.reset(NULL);
  }
}

gboolean WidgetGtk::OnDragMotion(GtkWidget* widget,
                                 GdkDragContext* context,
                                 gint x,
                                 gint y,
                                 guint time) {
  if (!drop_target_.get())
    drop_target_.reset(new DropTargetGtk(GetRootView(), context));
  return drop_target_->OnDragMotion(context, x, y, time);
}

gboolean WidgetGtk::OnEnterNotify(GtkWidget* widget, GdkEventCrossing* event) {
  if (last_mouse_event_was_move_ && last_mouse_move_x_ == event->x_root &&
      last_mouse_move_y_ == event->y_root) {
    // Don't generate a mouse event for the same location as the last.
    return false;
  }

  if (!last_mouse_event_was_move_ && !is_mouse_down_) {
    // When a mouse button is pressed gtk generates a leave, enter, press.
    // RootView expects to get a mouse move before a press, otherwise enter is
    // not set. So we generate a move here.
    last_mouse_move_x_ = event->x_root;
    last_mouse_move_y_ = event->y_root;
    last_mouse_event_was_move_ = true;

    int x = 0, y = 0;
    GetContainedWidgetEventCoordinates(event, &x, &y);

    // If this event is the result of pressing a button then one of the button
    // modifiers is set. Unset it as we're compensating for the leave generated
    // when you press a button.
    int flags = (Event::GetFlagsFromGdkState(event->state) &
                 ~(Event::EF_LEFT_BUTTON_DOWN |
                   Event::EF_MIDDLE_BUTTON_DOWN |
                   Event::EF_RIGHT_BUTTON_DOWN));
    MouseEvent mouse_move(Event::ET_MOUSE_MOVED, x, y, flags);
    root_view_->OnMouseMoved(mouse_move);
  }

  return false;
}

gboolean WidgetGtk::OnLeaveNotify(GtkWidget* widget, GdkEventCrossing* event) {
  last_mouse_event_was_move_ = false;
  if (!has_capture_ && !is_mouse_down_)
    root_view_->ProcessOnMouseExited();
  return false;
}

gboolean WidgetGtk::OnMotionNotify(GtkWidget* widget, GdkEventMotion* event) {
  int x = 0, y = 0;
  GetContainedWidgetEventCoordinates(event, &x, &y);

  if (has_capture_ && is_mouse_down_) {
    last_mouse_event_was_move_ = false;
    int flags = Event::GetFlagsFromGdkState(event->state);
    MouseEvent mouse_drag(Event::ET_MOUSE_DRAGGED, x, y, flags);
    root_view_->OnMouseDragged(mouse_drag);
    return true;
  }
  gfx::Point screen_loc(event->x_root, event->y_root);
  if (last_mouse_event_was_move_ && last_mouse_move_x_ == screen_loc.x() &&
      last_mouse_move_y_ == screen_loc.y()) {
    // Don't generate a mouse event for the same location as the last.
    return true;
  }
  last_mouse_move_x_ = screen_loc.x();
  last_mouse_move_y_ = screen_loc.y();
  last_mouse_event_was_move_ = true;
  int flags = Event::GetFlagsFromGdkState(event->state);
  MouseEvent mouse_move(Event::ET_MOUSE_MOVED, x, y, flags);
  root_view_->OnMouseMoved(mouse_move);
  return true;
}

gboolean WidgetGtk::OnButtonPress(GtkWidget* widget, GdkEventButton* event) {
  return ProcessMousePressed(event);
}

gboolean WidgetGtk::OnButtonRelease(GtkWidget* widget, GdkEventButton* event) {
  ProcessMouseReleased(event);
  return true;
}

gboolean WidgetGtk::OnScroll(GtkWidget* widget, GdkEventScroll* event) {
  return ProcessScroll(event);
}

gboolean WidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) {
  if (has_focus_)
    return false;  // This is the second focus-in event in a row, ignore it.
  has_focus_ = true;

  if (type_ == TYPE_CHILD)
    return false;

  // See description of got_initial_focus_in_ for details on this.
  if (!got_initial_focus_in_) {
    got_initial_focus_in_ = true;
    SetInitialFocus();
  } else {
    focus_manager_->RestoreFocusedView();
  }
  return false;
}

gboolean WidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) {
  if (!has_focus_)
    return false;  // This is the second focus-out event in a row, ignore it.
  has_focus_ = false;

  if (type_ == TYPE_CHILD)
    return false;

  // The top-level window lost focus, store the focused view.
  focus_manager_->StoreFocusedView();
  return false;
}

gboolean WidgetGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) {
  KeyEvent key_event(event);
  return root_view_->ProcessKeyEvent(key_event);
}

gboolean WidgetGtk::OnKeyRelease(GtkWidget* widget, GdkEventKey* event) {
  KeyEvent key_event(event);
  return root_view_->ProcessKeyEvent(key_event);
}

gboolean WidgetGtk::OnQueryTooltip(GtkWidget* widget,
                                   gint x,
                                   gint y,
                                   gboolean keyboard_mode,
                                   GtkTooltip* tooltip) {
  return tooltip_manager_->ShowTooltip(x, y, keyboard_mode, tooltip);
}

gboolean WidgetGtk::OnVisibilityNotify(GtkWidget* widget,
                                       GdkEventVisibility* event) {
  return false;
}

gboolean WidgetGtk::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) {
  HandleGrabBroke();
  return false;  // To let other widgets get the event.
}

void WidgetGtk::OnGrabNotify(GtkWidget* widget, gboolean was_grabbed) {
  if (!window_contents_)
    return;  // Grab broke after window destroyed, don't try processing it.

  gtk_grab_remove(window_contents_);
  HandleGrabBroke();
}

void WidgetGtk::OnDestroy(GtkWidget* object) {
  // Note that this handler is hooked to GtkObject::destroy.
  widget_ = window_contents_ = NULL;
  if (delete_on_destroy_) {
    // Delays the deletion of this WidgetGtk as we want its children to have
    // access to it when destroyed.
    MessageLoop::current()->DeleteSoon(FROM_HERE, this);
  }
}

void WidgetGtk::OnShow(GtkWidget* widget) {
}

void WidgetGtk::OnHide(GtkWidget* widget) {
}

void WidgetGtk::DoGrab() {
  has_capture_ = true;
  gtk_grab_add(window_contents_);
}

void WidgetGtk::ReleaseGrab() {
  if (has_capture_) {
    has_capture_ = false;
    gtk_grab_remove(window_contents_);
  }
}

////////////////////////////////////////////////////////////////////////////////
// WidgetGtk, private:

RootView* WidgetGtk::CreateRootView() {
  return new RootView(this);
}

gboolean WidgetGtk::OnWindowPaint(GtkWidget* widget, GdkEventExpose* event) {
  // Clear the background to be totally transparent. We don't need to
  // paint the root view here as that is done by OnPaint.
  DCHECK(transparent_);
  DrawTransparentBackground(widget, event);
  return false;
}

bool WidgetGtk::ProcessMousePressed(GdkEventButton* event) {
  if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
    // The sequence for double clicks is press, release, press, 2press, release.
    // This means that at the time we get the second 'press' we don't know
    // whether it corresponds to a double click or not. For now we're completely
    // ignoring the 2press/3press events as they are duplicate. To make this
    // work right we need to write our own code that detects if the press is a
    // double/triple. For now we're completely punting, which means we always
    // get single clicks.
    // TODO: fix this.
    return true;
  }

  // An event may come from a contained widget which has its own gdk window.
  // Translate it to the widget's coordinates.
  int x = 0;
  int y = 0;
  GetContainedWidgetEventCoordinates(event, &x, &y);
  last_mouse_event_was_move_ = false;
  MouseEvent mouse_pressed(Event::ET_MOUSE_PRESSED, x, y,
                           GetFlagsForEventButton(*event));

  if (root_view_->OnMousePressed(mouse_pressed)) {
    is_mouse_down_ = true;
    if (!has_capture_)
      DoGrab();
    return true;
  }

  // Returns true to consume the event when widget is not transparent.
  return !transparent_;
}

void WidgetGtk::ProcessMouseReleased(GdkEventButton* event) {
  int x = 0, y = 0;
  GetContainedWidgetEventCoordinates(event, &x, &y);

  last_mouse_event_was_move_ = false;
  MouseEvent mouse_up(Event::ET_MOUSE_RELEASED, x, y,
                      GetFlagsForEventButton(*event));
  // Release the capture first, that way we don't get confused if
  // OnMouseReleased blocks.
  if (has_capture_ && ReleaseCaptureOnMouseReleased())
    ReleaseGrab();
  is_mouse_down_ = false;
  // GTK generates a mouse release at the end of dnd. We need to ignore it.
  if (!drag_data_)
    root_view_->OnMouseReleased(mouse_up, false);
}

bool WidgetGtk::ProcessScroll(GdkEventScroll* event) {
  // An event may come from a contained widget which has its own gdk window.
  // Translate it to the widget's coordinates.
  int x = 0, y = 0;
  GetContainedWidgetEventCoordinates(event, &x, &y);
  int flags = Event::GetFlagsFromGdkState(event->state);
  int increment = 0;
  bool is_horizontal = true;
  switch (event->direction) {
    case GDK_SCROLL_UP:
      increment = -1;
      is_horizontal = false;
      break;
    case GDK_SCROLL_DOWN:
      increment = 1;
      is_horizontal = false;
      break;
    case GDK_SCROLL_LEFT:
      increment = -1;
      is_horizontal = true;
      break;
    case GDK_SCROLL_RIGHT:
      increment = 1;
      is_horizontal = false;
      break;
  }
  increment *= is_horizontal ? root_view_->width() / 5 :
      root_view_->height() / 5;
  MouseWheelEvent wheel_event(increment, x, y, flags);
  return root_view_->ProcessMouseWheelEvent(wheel_event);
}

// static
void WidgetGtk::SetRootViewForWidget(GtkWidget* widget, RootView* root_view) {
  g_object_set_data(G_OBJECT(widget), "root-view", root_view);
}

// static
Window* WidgetGtk::GetWindowImpl(GtkWidget* widget) {
  GtkWidget* parent = widget;
  while (parent) {
    WidgetGtk* widget_gtk = GetViewForNative(parent);
    if (widget_gtk && widget_gtk->is_window_)
      return static_cast<WindowGtk*>(widget_gtk);
    parent = gtk_widget_get_parent(parent);
  }
  return NULL;
}

void WidgetGtk::CreateGtkWidget(GtkWidget* parent, const gfx::Rect& bounds) {
  // We turn off double buffering for two reasons:
  // 1. We draw to a canvas then composite to the screen, which means we're
  //    doing our own double buffering already.
  // 2. GTKs double buffering clips to the dirty region. RootView occasionally
  //    needs to expand the paint region (see RootView::OnPaint). This means
  //    that if we use GTK's double buffering and we tried to expand the dirty
  //    region, it wouldn't get painted.
  if (type_ == TYPE_CHILD) {
    window_contents_ = widget_ = gtk_views_fixed_new();
    gtk_widget_set_name(widget_, "views-gtkwidget-child-fixed");
    if (!is_double_buffered_)
      GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED);
    gtk_fixed_set_has_window(GTK_FIXED(widget_), true);
    if (!parent && !null_parent_) {
      GtkWidget* popup = gtk_window_new(GTK_WINDOW_POPUP);
      null_parent_ = gtk_fixed_new();
      gtk_widget_set_name(widget_, "views-gtkwidget-null-parent");
      gtk_container_add(GTK_CONTAINER(popup), null_parent_);
      gtk_widget_realize(null_parent_);
    }
    if (transparent_) {
      // transparency has to be configured before widget is realized.
      DCHECK(parent) << "Transparent widget must have parent when initialized";
      ConfigureWidgetForTransparentBackground(parent);
    }
    gtk_container_add(GTK_CONTAINER(parent ? parent : null_parent_), widget_);
    gtk_widget_realize(widget_);
    if (transparent_) {
      // The widget has to be realized to set composited flag.
      // I tried "realize" signal to set this flag, but it did not work
      // when the top level is popup.
      CompositePainter::SetComposited(widget_);
    }
  } else {
    widget_ = gtk_window_new(
        (type_ == TYPE_WINDOW || type_ == TYPE_DECORATED_WINDOW) ?
        GTK_WINDOW_TOPLEVEL : GTK_WINDOW_POPUP);
    gtk_widget_set_name(widget_, "views-gtkwidget-window");
    if (transient_to_parent_)
      gtk_window_set_transient_for(GTK_WINDOW(widget_), GTK_WINDOW(parent));
    GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED);

    if (!bounds.size().IsEmpty()) {
      // When we realize the window, the window manager is given a size. If we
      // don't specify a size before then GTK defaults to 200x200. Specify
      // a size now so that the window manager sees the requested size.
      GtkAllocation alloc = { 0, 0, bounds.width(), bounds.height() };
      gtk_widget_size_allocate(widget_, &alloc);
    }
    if (type_ != TYPE_DECORATED_WINDOW) {
      gtk_window_set_decorated(GTK_WINDOW(widget_), false);
      // We'll take care of positioning our window.
      gtk_window_set_position(GTK_WINDOW(widget_), GTK_WIN_POS_NONE);
    }

    window_contents_ = gtk_views_fixed_new();
    gtk_widget_set_name(window_contents_, "views-gtkwidget-window-fixed");
    if (!is_double_buffered_)
      GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED);
    gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true);
    gtk_container_add(GTK_CONTAINER(widget_), window_contents_);
    gtk_widget_show(window_contents_);
    g_object_set_data(G_OBJECT(window_contents_), kWidgetKey,
                      static_cast<Widget*>(this));

    if (transparent_)
      ConfigureWidgetForTransparentBackground(NULL);

    if (ignore_events_)
      ConfigureWidgetForIgnoreEvents();

    SetAlwaysOnTop(always_on_top_);
    // The widget needs to be realized before handlers like size-allocate can
    // function properly.
    gtk_widget_realize(widget_);
  }
  // Setting the WidgetKey property to widget_, which is used by
  // GetWidgetFromNativeWindow.
  SetNativeWindowProperty(kWidgetWideKey, this);
}

void WidgetGtk::ConfigureWidgetForTransparentBackground(GtkWidget* parent) {
  DCHECK(widget_ && window_contents_);

  GdkColormap* rgba_colormap =
      gdk_screen_get_rgba_colormap(gtk_widget_get_screen(widget_));
  if (!rgba_colormap) {
    transparent_ = false;
    return;
  }
  // To make the background transparent we need to install the RGBA colormap
  // on both the window and fixed. In addition we need to make sure no
  // decorations are drawn. The last bit is to make sure the widget doesn't
  // attempt to draw a pixmap in it's background.
  if (type_ != TYPE_CHILD) {
    DCHECK(parent == NULL);
    gtk_widget_set_colormap(widget_, rgba_colormap);
    gtk_widget_set_app_paintable(widget_, true);
    g_signal_connect(widget_, "expose_event",
                     G_CALLBACK(&OnWindowPaintThunk), this);
    gtk_widget_realize(widget_);
    gdk_window_set_decorations(widget_->window,
                               static_cast<GdkWMDecoration>(0));
  } else {
    DCHECK(parent);
    CompositePainter::AddCompositePainter(parent);
  }
  DCHECK(!GTK_WIDGET_REALIZED(window_contents_));
  gtk_widget_set_colormap(window_contents_, rgba_colormap);
}

void WidgetGtk::ConfigureWidgetForIgnoreEvents() {
  gtk_widget_realize(widget_);
  GdkWindow* gdk_window = widget_->window;
  Display* display = GDK_WINDOW_XDISPLAY(gdk_window);
  XID win = GDK_WINDOW_XID(gdk_window);

  // This sets the clickable area to be empty, allowing all events to be
  // passed to any windows behind this one.
  XShapeCombineRectangles(
      display,
      win,
      ShapeInput,
      0,  // x offset
      0,  // y offset
      NULL,  // rectangles
      0,  // num rectangles
      ShapeSet,
      0);
}

void WidgetGtk::HandleGrabBroke() {
  if (has_capture_) {
    if (is_mouse_down_)
      root_view_->ProcessMouseDragCanceled();
    is_mouse_down_ = false;
    has_capture_ = false;
  }
}

void WidgetGtk::DrawTransparentBackground(GtkWidget* widget,
                                          GdkEventExpose* event) {
  cairo_t* cr = gdk_cairo_create(widget->window);
  cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
  gdk_cairo_region(cr, event->region);
  cairo_fill(cr);
  cairo_destroy(cr);
}

////////////////////////////////////////////////////////////////////////////////
// Widget, public:

// static
Widget* Widget::CreatePopupWidget(TransparencyParam transparent,
                                  EventsParam accept_events,
                                  DeleteParam delete_on_destroy) {
  WidgetGtk* popup = new WidgetGtk(WidgetGtk::TYPE_POPUP);
  popup->set_delete_on_destroy(delete_on_destroy == DeleteOnDestroy);
  if (transparent == Transparent)
    popup->MakeTransparent();
  if (accept_events == NotAcceptEvents)
    popup->MakeIgnoreEvents();
  return popup;
}

// Callback from gtk_container_foreach. Locates the first root view of widget
// or one of it's descendants.
static void RootViewLocatorCallback(GtkWidget* widget,
                                    gpointer root_view_p) {
  RootView** root_view = static_cast<RootView**>(root_view_p);
  if (!*root_view) {
    *root_view = WidgetGtk::GetRootViewForWidget(widget);
    if (!*root_view && GTK_IS_CONTAINER(widget)) {
      // gtk_container_foreach only iterates over children, not all descendants,
      // so we have to recurse here to get all descendants.
      gtk_container_foreach(GTK_CONTAINER(widget), RootViewLocatorCallback,
                            root_view_p);
    }
  }
}

// static
RootView* Widget::FindRootView(GtkWindow* window) {
  RootView* root_view = WidgetGtk::GetRootViewForWidget(GTK_WIDGET(window));
  if (root_view)
    return root_view;

  // Enumerate all children and check if they have a RootView.
  gtk_container_foreach(GTK_CONTAINER(window), RootViewLocatorCallback,
                        static_cast<gpointer>(&root_view));
  return root_view;
}

static void AllRootViewsLocatorCallback(GtkWidget* widget,
                                        gpointer root_view_p) {
  std::set<RootView*>* root_views_set =
      reinterpret_cast<std::set<RootView*>*>(root_view_p);
  RootView *root_view = WidgetGtk::GetRootViewForWidget(widget);
  if (!root_view && GTK_IS_CONTAINER(widget)) {
    // gtk_container_foreach only iterates over children, not all descendants,
    // so we have to recurse here to get all descendants.
    gtk_container_foreach(GTK_CONTAINER(widget), AllRootViewsLocatorCallback,
        root_view_p);
  } else {
    if (root_view)
      root_views_set->insert(root_view);
  }
}

// static
void Widget::FindAllRootViews(GtkWindow* window,
                              std::vector<RootView*>* root_views) {
  RootView* root_view = WidgetGtk::GetRootViewForWidget(GTK_WIDGET(window));
  if (root_view)
    root_views->push_back(root_view);

  std::set<RootView*> root_views_set;

  // Enumerate all children and check if they have a RootView.
  gtk_container_foreach(GTK_CONTAINER(window), AllRootViewsLocatorCallback,
      reinterpret_cast<gpointer>(&root_views_set));
  root_views->clear();
  root_views->reserve(root_views_set.size());
  for (std::set<RootView*>::iterator it = root_views_set.begin();
      it != root_views_set.end();
      ++it)
    root_views->push_back(*it);
}

// static
Widget* Widget::GetWidgetFromNativeView(gfx::NativeView native_view) {
  gpointer raw_widget = g_object_get_data(G_OBJECT(native_view), kWidgetKey);
  if (raw_widget)
    return reinterpret_cast<Widget*>(raw_widget);
  return NULL;
}

// static
Widget* Widget::GetWidgetFromNativeWindow(gfx::NativeWindow native_window) {
  gpointer raw_widget = g_object_get_data(G_OBJECT(native_window), kWidgetKey);
  if (raw_widget)
    return reinterpret_cast<Widget*>(raw_widget);
  return NULL;
}

// static
void Widget::NotifyLocaleChanged() {
  GList *window_list = gtk_window_list_toplevels();
  for (GList* element = window_list; element; element = g_list_next(element)) {
    GtkWindow* window = GTK_WINDOW(element->data);
    DCHECK(window);
    RootView *root_view = FindRootView(window);
    if (root_view)
      root_view->NotifyLocaleChanged();
  }
  g_list_free(window_list);
}

}  // namespace views