This source file includes following definitions.
- HasArrow
- IsArrowLeft
- IsArrowMiddle
- IsArrowRight
- IsArrowTop
- IsArrowBottom
- IsFixed
- AdjustFrameStyleForLocale
- Show
- weak_ptr_factory_
- Init
- MakeFramePolygonPoints
- GetAllowedFrameStyle
- UpdateFrameStyle
- UpdateWindowShape
- MoveWindow
- StackWindow
- Observe
- StopGrabbingInput
- GetNativeWindow
- Close
- SetPositionRelativeToAnchor
- GrabPointerAndKeyboard
- UngrabPointerAndKeyboard
- OnGtkAccelerator
- OnExpose
- OnSizeAllocate
- OnButtonPress
- OnDestroy
- OnHide
- OnGrabBroken
- OnForeshadowWidgetHide
- OnToplevelConfigure
- OnToplevelUnmap
- OnAnchorAllocate
- OnAnchorDestroy
#include "chrome/browser/ui/gtk/bubble/bubble_gtk.h"
#include <gdk/gdkkeysyms.h>
#include "base/bind.h"
#include "base/i18n/rtl.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/ui/gtk/bubble/bubble_accelerators_gtk.h"
#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "content/public/browser/notification_source.h"
#include "ui/base/gtk/gtk_hig_constants.h"
#include "ui/base/gtk/gtk_windowing.h"
#include "ui/gfx/gtk_compat.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/path.h"
#include "ui/gfx/rect.h"
namespace {
const int kArrowSize = 8;
const int kArrowX = 18;
const int kArrowToContentPadding = -4;
const int kCornerSize = 3;
const int kFixedPositionPaddingEnd = 10;
const int kFixedPositionPaddingTop = 5;
const GdkColor kBackgroundColor = GDK_COLOR_RGB(0xff, 0xff, 0xff);
const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63);
bool HasArrow(BubbleGtk::FrameStyle frame_style) {
return frame_style != BubbleGtk::FLOAT_BELOW_RECT &&
frame_style != BubbleGtk::CENTER_OVER_RECT &&
frame_style != BubbleGtk::FIXED_TOP_LEFT &&
frame_style != BubbleGtk::FIXED_TOP_RIGHT;
}
bool IsArrowLeft(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::ANCHOR_TOP_LEFT ||
frame_style == BubbleGtk::ANCHOR_BOTTOM_LEFT;
}
bool IsArrowMiddle(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::ANCHOR_TOP_MIDDLE ||
frame_style == BubbleGtk::ANCHOR_BOTTOM_MIDDLE;
}
bool IsArrowRight(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::ANCHOR_TOP_RIGHT ||
frame_style == BubbleGtk::ANCHOR_BOTTOM_RIGHT;
}
bool IsArrowTop(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::ANCHOR_TOP_LEFT ||
frame_style == BubbleGtk::ANCHOR_TOP_MIDDLE ||
frame_style == BubbleGtk::ANCHOR_TOP_RIGHT;
}
bool IsArrowBottom(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::ANCHOR_BOTTOM_LEFT ||
frame_style == BubbleGtk::ANCHOR_BOTTOM_MIDDLE ||
frame_style == BubbleGtk::ANCHOR_BOTTOM_RIGHT;
}
bool IsFixed(BubbleGtk::FrameStyle frame_style) {
return frame_style == BubbleGtk::FIXED_TOP_LEFT ||
frame_style == BubbleGtk::FIXED_TOP_RIGHT;
}
BubbleGtk::FrameStyle AdjustFrameStyleForLocale(
BubbleGtk::FrameStyle frame_style) {
if (!base::i18n::IsRTL())
return frame_style;
switch (frame_style) {
case BubbleGtk::ANCHOR_TOP_MIDDLE:
case BubbleGtk::ANCHOR_BOTTOM_MIDDLE:
case BubbleGtk::FLOAT_BELOW_RECT:
case BubbleGtk::CENTER_OVER_RECT:
return frame_style;
case BubbleGtk::ANCHOR_TOP_LEFT:
return BubbleGtk::ANCHOR_TOP_RIGHT;
case BubbleGtk::ANCHOR_TOP_RIGHT:
return BubbleGtk::ANCHOR_TOP_LEFT;
case BubbleGtk::ANCHOR_BOTTOM_LEFT:
return BubbleGtk::ANCHOR_BOTTOM_RIGHT;
case BubbleGtk::ANCHOR_BOTTOM_RIGHT:
return BubbleGtk::ANCHOR_BOTTOM_LEFT;
case BubbleGtk::FIXED_TOP_LEFT:
return BubbleGtk::FIXED_TOP_RIGHT;
case BubbleGtk::FIXED_TOP_RIGHT:
return BubbleGtk::FIXED_TOP_LEFT;
}
NOTREACHED();
return BubbleGtk::ANCHOR_TOP_LEFT;
}
}
BubbleGtk* BubbleGtk::Show(GtkWidget* anchor_widget,
const gfx::Rect* rect,
GtkWidget* content,
FrameStyle frame_style,
int attribute_flags,
GtkThemeService* provider,
BubbleDelegateGtk* delegate) {
BubbleGtk* bubble = new BubbleGtk(provider,
AdjustFrameStyleForLocale(frame_style),
attribute_flags);
bubble->Init(anchor_widget, rect, content, attribute_flags);
bubble->set_delegate(delegate);
return bubble;
}
BubbleGtk::BubbleGtk(GtkThemeService* provider,
FrameStyle frame_style,
int attribute_flags)
: delegate_(NULL),
window_(NULL),
theme_service_(provider),
accel_group_(gtk_accel_group_new()),
toplevel_window_(NULL),
anchor_widget_(NULL),
mask_region_(NULL),
requested_frame_style_(frame_style),
actual_frame_style_(ANCHOR_TOP_LEFT),
match_system_theme_(attribute_flags & MATCH_SYSTEM_THEME),
grab_input_(attribute_flags & GRAB_INPUT),
closed_by_escape_(false),
weak_ptr_factory_(this) {}
BubbleGtk::~BubbleGtk() {
if (delegate_)
delegate_->BubbleClosing(this, closed_by_escape_);
g_object_unref(accel_group_);
if (mask_region_)
gdk_region_destroy(mask_region_);
}
void BubbleGtk::Init(GtkWidget* anchor_widget,
const gfx::Rect* rect,
GtkWidget* content,
int attribute_flags) {
GtkWidget* current_grab_widget = gtk_grab_get_current();
if (current_grab_widget)
gtk_widget_hide(current_grab_widget);
DCHECK(!window_);
anchor_widget_ = anchor_widget;
toplevel_window_ = gtk_widget_get_toplevel(anchor_widget_);
DCHECK(gtk_widget_is_toplevel(toplevel_window_));
rect_ = rect ? *rect : gtk_util::WidgetBounds(anchor_widget);
window_ = gtk_window_new(attribute_flags & POPUP_WINDOW ?
GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL);
gtk_widget_set_app_paintable(window_, TRUE);
gtk_window_set_resizable(GTK_WINDOW(window_), FALSE);
if (!(attribute_flags & NO_ACCELERATORS)) {
for (BubbleAcceleratorsGtk::const_iterator
i(BubbleAcceleratorsGtk::begin());
i != BubbleAcceleratorsGtk::end();
++i) {
gtk_accel_group_connect(accel_group_,
i->keyval,
i->modifier_type,
GtkAccelFlags(0),
g_cclosure_new(G_CALLBACK(&OnGtkAcceleratorThunk),
this,
NULL));
}
gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group_);
}
int arrow_padding = HasArrow(requested_frame_style_) ? kArrowSize : 0;
GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), arrow_padding, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(alignment), content);
gtk_container_add(GTK_CONTAINER(window_), alignment);
gtk_widget_realize(window_);
UpdateFrameStyle(true);
StackWindow();
gtk_widget_add_events(window_, GDK_BUTTON_PRESS_MASK);
signals_.ConnectAfter(window_, "expose-event",
G_CALLBACK(OnExposeThunk), this);
signals_.Connect(window_, "size-allocate", G_CALLBACK(OnSizeAllocateThunk),
this);
signals_.Connect(window_, "button-press-event",
G_CALLBACK(OnButtonPressThunk), this);
signals_.Connect(window_, "destroy", G_CALLBACK(OnDestroyThunk), this);
signals_.Connect(window_, "hide", G_CALLBACK(OnHideThunk), this);
if (grab_input_) {
signals_.Connect(window_, "grab-broken-event",
G_CALLBACK(OnGrabBrokenThunk), this);
}
signals_.Connect(anchor_widget_, "destroy",
G_CALLBACK(OnAnchorDestroyThunk), this);
if (anchor_widget_ != toplevel_window_) {
signals_.Connect(anchor_widget_, "size-allocate",
G_CALLBACK(OnAnchorAllocateThunk), this);
}
signals_.Connect(toplevel_window_, "configure-event",
G_CALLBACK(OnToplevelConfigureThunk), this);
signals_.Connect(toplevel_window_, "unmap-event",
G_CALLBACK(OnToplevelUnmapThunk), this);
gtk_widget_show_all(window_);
if (grab_input_)
gtk_grab_add(window_);
GrabPointerAndKeyboard();
registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
content::Source<ThemeService>(theme_service_));
theme_service_->InitThemesFor(this);
}
std::vector<GdkPoint> BubbleGtk::MakeFramePolygonPoints(
FrameStyle frame_style,
int width,
int height,
FrameType type) {
using gtk_util::MakeBidiGdkPoint;
std::vector<GdkPoint> points;
int top_arrow_size = IsArrowTop(frame_style) ? kArrowSize : 0;
int bottom_arrow_size = IsArrowBottom(frame_style) ? kArrowSize : 0;
bool on_left = IsArrowLeft(frame_style);
int y_off = type == FRAME_MASK ? 0 : -1;
int x_off_l = on_left ? y_off : 0;
int x_off_r = !on_left ? -y_off : 0;
points.push_back(MakeBidiGdkPoint(
x_off_r, top_arrow_size + kCornerSize - 1, width, on_left));
points.push_back(MakeBidiGdkPoint(
kCornerSize + x_off_r - 1, top_arrow_size, width, on_left));
if (top_arrow_size) {
int arrow_x = frame_style == ANCHOR_TOP_MIDDLE ? width / 2 : kArrowX;
points.push_back(MakeBidiGdkPoint(
arrow_x - top_arrow_size + x_off_r, top_arrow_size, width, on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x + x_off_r, 0, width, on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x + 1 + x_off_l, 0, width, on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x + top_arrow_size + 1 + x_off_l, top_arrow_size,
width, on_left));
}
points.push_back(MakeBidiGdkPoint(
width - kCornerSize + 1 + x_off_l, top_arrow_size, width, on_left));
points.push_back(MakeBidiGdkPoint(
width + x_off_l, top_arrow_size + kCornerSize - 1, width, on_left));
points.push_back(MakeBidiGdkPoint(
width + x_off_l, height - bottom_arrow_size - kCornerSize,
width, on_left));
points.push_back(MakeBidiGdkPoint(
width - kCornerSize + x_off_r, height - bottom_arrow_size + y_off,
width, on_left));
if (bottom_arrow_size) {
int arrow_x = frame_style == ANCHOR_BOTTOM_MIDDLE ?
width / 2 : kArrowX;
points.push_back(MakeBidiGdkPoint(
arrow_x + bottom_arrow_size + 1 + x_off_l,
height - bottom_arrow_size + y_off,
width,
on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x + 1 + x_off_l, height + y_off, width, on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x + x_off_r, height + y_off, width, on_left));
points.push_back(MakeBidiGdkPoint(
arrow_x - bottom_arrow_size + x_off_r,
height - bottom_arrow_size + y_off,
width,
on_left));
}
points.push_back(MakeBidiGdkPoint(
kCornerSize + x_off_l, height -bottom_arrow_size + y_off,
width, on_left));
points.push_back(MakeBidiGdkPoint(
x_off_r, height - bottom_arrow_size - kCornerSize, width, on_left));
return points;
}
BubbleGtk::FrameStyle BubbleGtk::GetAllowedFrameStyle(
FrameStyle preferred_style,
int arrow_x,
int arrow_y,
int width,
int height) {
if (IsFixed(preferred_style))
return preferred_style;
const int screen_width = gdk_screen_get_width(gdk_screen_get_default());
const int screen_height = gdk_screen_get_height(gdk_screen_get_default());
const bool prefer_top_arrow = IsArrowTop(preferred_style) ||
preferred_style == FLOAT_BELOW_RECT;
const int top_arrow_bleed =
std::max(height + kArrowSize + arrow_y - screen_height, 0);
const int bottom_arrow_bleed = std::max(height + kArrowSize - arrow_y, 0);
FrameStyle frame_style_none = FLOAT_BELOW_RECT;
FrameStyle frame_style_left = ANCHOR_TOP_LEFT;
FrameStyle frame_style_middle = ANCHOR_TOP_MIDDLE;
FrameStyle frame_style_right = ANCHOR_TOP_RIGHT;
if ((prefer_top_arrow && (top_arrow_bleed > bottom_arrow_bleed)) ||
(!prefer_top_arrow && (top_arrow_bleed >= bottom_arrow_bleed))) {
frame_style_none = CENTER_OVER_RECT;
frame_style_left = ANCHOR_BOTTOM_LEFT;
frame_style_middle = ANCHOR_BOTTOM_MIDDLE;
frame_style_right = ANCHOR_BOTTOM_RIGHT;
}
if (!HasArrow(preferred_style))
return frame_style_none;
if (IsArrowMiddle(preferred_style))
return frame_style_middle;
const bool prefer_left_arrow = IsArrowLeft(preferred_style);
const int left_arrow_bleed =
std::max(width + arrow_x - kArrowX - screen_width, 0);
const int right_arrow_bleed = std::max(width - arrow_x - kArrowX, 0);
return ((prefer_left_arrow && (left_arrow_bleed <= right_arrow_bleed)) ||
(!prefer_left_arrow && (left_arrow_bleed < right_arrow_bleed))) ?
frame_style_left : frame_style_right;
}
bool BubbleGtk::UpdateFrameStyle(bool force_move_and_reshape) {
if (!toplevel_window_ || !anchor_widget_)
return false;
gint toplevel_x = 0, toplevel_y = 0;
gdk_window_get_position(gtk_widget_get_window(toplevel_window_),
&toplevel_x, &toplevel_y);
int offset_x, offset_y;
gtk_widget_translate_coordinates(anchor_widget_, toplevel_window_,
rect_.x(), rect_.y(), &offset_x, &offset_y);
FrameStyle old_frame_style = actual_frame_style_;
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
actual_frame_style_ = GetAllowedFrameStyle(
requested_frame_style_,
toplevel_x + offset_x + (rect_.width() / 2),
toplevel_y + offset_y,
allocation.width,
allocation.height);
if (force_move_and_reshape || actual_frame_style_ != old_frame_style) {
UpdateWindowShape();
MoveWindow();
gtk_widget_queue_draw(window_);
return true;
}
return false;
}
void BubbleGtk::UpdateWindowShape() {
if (mask_region_) {
gdk_region_destroy(mask_region_);
mask_region_ = NULL;
}
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
std::vector<GdkPoint> points = MakeFramePolygonPoints(
actual_frame_style_, allocation.width, allocation.height,
FRAME_MASK);
mask_region_ = gdk_region_polygon(&points[0],
points.size(),
GDK_EVEN_ODD_RULE);
GdkWindow* gdk_window = gtk_widget_get_window(window_);
gdk_window_shape_combine_region(gdk_window, NULL, 0, 0);
gdk_window_shape_combine_region(gdk_window, mask_region_, 0, 0);
}
void BubbleGtk::MoveWindow() {
if (!toplevel_window_ || !anchor_widget_)
return;
gint toplevel_x = 0, toplevel_y = 0;
gdk_window_get_position(gtk_widget_get_window(toplevel_window_),
&toplevel_x, &toplevel_y);
int offset_x, offset_y;
gtk_widget_translate_coordinates(anchor_widget_, toplevel_window_,
rect_.x(), rect_.y(), &offset_x, &offset_y);
gint screen_x = 0;
if (IsFixed(actual_frame_style_)) {
GtkAllocation toplevel_allocation;
gtk_widget_get_allocation(toplevel_window_, &toplevel_allocation);
GtkAllocation bubble_allocation;
gtk_widget_get_allocation(window_, &bubble_allocation);
int x_offset = actual_frame_style_ == FIXED_TOP_LEFT ?
kFixedPositionPaddingEnd :
toplevel_allocation.width - bubble_allocation.width -
kFixedPositionPaddingEnd;
screen_x = toplevel_x + x_offset;
} else if (!HasArrow(actual_frame_style_) ||
IsArrowMiddle(actual_frame_style_)) {
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
screen_x =
toplevel_x + offset_x + (rect_.width() / 2) - allocation.width / 2;
} else if (IsArrowLeft(actual_frame_style_)) {
screen_x = toplevel_x + offset_x + (rect_.width() / 2) - kArrowX;
} else if (IsArrowRight(actual_frame_style_)) {
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
screen_x = toplevel_x + offset_x + (rect_.width() / 2) -
allocation.width + kArrowX;
} else {
NOTREACHED();
}
gint screen_y = toplevel_y + offset_y + rect_.height();
if (IsFixed(actual_frame_style_)) {
screen_y = toplevel_y + kFixedPositionPaddingTop;
} else if (IsArrowTop(actual_frame_style_) ||
actual_frame_style_ == FLOAT_BELOW_RECT) {
screen_y += kArrowToContentPadding;
} else {
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
screen_y -= allocation.height + kArrowToContentPadding;
}
gtk_window_move(GTK_WINDOW(window_), screen_x, screen_y);
}
void BubbleGtk::StackWindow() {
if (toplevel_window_)
ui::StackPopupWindow(window_, toplevel_window_);
}
void BubbleGtk::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_EQ(type, chrome::NOTIFICATION_BROWSER_THEME_CHANGED);
if (theme_service_->UsingNativeTheme() && match_system_theme_) {
gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, NULL);
} else {
gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &kBackgroundColor);
}
}
void BubbleGtk::StopGrabbingInput() {
UngrabPointerAndKeyboard();
if (!grab_input_)
return;
grab_input_ = false;
gtk_grab_remove(window_);
}
GtkWindow* BubbleGtk::GetNativeWindow() {
return GTK_WINDOW(window_);
}
void BubbleGtk::Close() {
DCHECK(window_);
gtk_widget_destroy(window_);
}
void BubbleGtk::SetPositionRelativeToAnchor(const gfx::Rect* rect) {
rect_ = rect ? *rect : gtk_util::WidgetBounds(anchor_widget_);
if (!UpdateFrameStyle(false))
MoveWindow();
}
void BubbleGtk::GrabPointerAndKeyboard() {
GdkWindow* gdk_window = gtk_widget_get_window(window_);
GdkGrabStatus pointer_grab_status =
gdk_pointer_grab(gdk_window,
TRUE,
GDK_BUTTON_PRESS_MASK,
NULL,
NULL,
GDK_CURRENT_TIME);
if (pointer_grab_status != GDK_GRAB_SUCCESS) {
DLOG(ERROR) << "Unable to grab pointer (status="
<< pointer_grab_status << ")";
}
if (grab_input_) {
GdkGrabStatus keyboard_grab_status =
gdk_keyboard_grab(gdk_window,
FALSE,
GDK_CURRENT_TIME);
if (keyboard_grab_status != GDK_GRAB_SUCCESS) {
DLOG(ERROR) << "Unable to grab keyboard (status="
<< keyboard_grab_status << ")";
}
}
}
void BubbleGtk::UngrabPointerAndKeyboard() {
gdk_pointer_ungrab(GDK_CURRENT_TIME);
if (grab_input_)
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
}
gboolean BubbleGtk::OnGtkAccelerator(GtkAccelGroup* group,
GObject* acceleratable,
guint keyval,
GdkModifierType modifier) {
GdkEventKey msg;
GdkKeymapKey* keys;
gint n_keys;
switch (keyval) {
case GDK_Escape:
closed_by_escape_ = true;
Close();
return TRUE;
case GDK_w:
if (modifier & GDK_CONTROL_MASK) {
gdk_keymap_get_entries_for_keyval(NULL,
keyval,
&keys,
&n_keys);
if (n_keys) {
msg.type = GDK_KEY_PRESS;
msg.window = gtk_widget_get_window(toplevel_window_);
msg.send_event = TRUE;
msg.time = GDK_CURRENT_TIME;
msg.state = modifier | GDK_MOD2_MASK;
msg.keyval = keyval;
msg.length = 0;
msg.string = NULL;
msg.hardware_keycode = keys[0].keycode;
msg.group = keys[0].group;
msg.is_modifier = 0;
g_free(keys);
gtk_main_do_event(reinterpret_cast<GdkEvent*>(&msg));
} else {
DLOG(WARNING) << "Found no keys for value " << keyval;
}
Close();
}
break;
default:
return FALSE;
}
return TRUE;
}
gboolean BubbleGtk::OnExpose(GtkWidget* widget, GdkEventExpose* expose) {
GdkDrawable* drawable = GDK_DRAWABLE(gtk_widget_get_window(window_));
GdkGC* gc = gdk_gc_new(drawable);
gdk_gc_set_rgb_fg_color(gc, &kFrameColor);
GtkAllocation allocation;
gtk_widget_get_allocation(window_, &allocation);
std::vector<GdkPoint> points = MakeFramePolygonPoints(
actual_frame_style_, allocation.width, allocation.height,
FRAME_STROKE);
gdk_draw_polygon(drawable, gc, FALSE, &points[0], points.size());
if (!grab_input_)
gdk_pointer_ungrab(GDK_CURRENT_TIME);
g_object_unref(gc);
return FALSE;
}
void BubbleGtk::OnSizeAllocate(GtkWidget* widget,
GtkAllocation* allocation) {
if (!UpdateFrameStyle(false)) {
UpdateWindowShape();
MoveWindow();
}
}
gboolean BubbleGtk::OnButtonPress(GtkWidget* widget,
GdkEventButton* event) {
GdkWindow* gdk_window = gtk_widget_get_window(window_);
if (event->window == gdk_window &&
(mask_region_ && gdk_region_point_in(mask_region_, event->x, event->y))) {
return FALSE;
}
if (event->window != gdk_window &&
gdk_window_get_toplevel(event->window) == gdk_window) {
return FALSE;
}
if (grab_input_) {
Close();
return TRUE;
}
return FALSE;
}
gboolean BubbleGtk::OnDestroy(GtkWidget* widget) {
delete this;
return FALSE;
}
void BubbleGtk::OnHide(GtkWidget* widget) {
gtk_widget_destroy(widget);
}
gboolean BubbleGtk::OnGrabBroken(GtkWidget* widget,
GdkEventGrabBroken* grab_broken) {
if (!grab_input_)
return FALSE;
if (!grab_broken->grab_window)
return FALSE;
gpointer user_data;
gdk_window_get_user_data(grab_broken->grab_window, &user_data);
if (GTK_IS_WIDGET(user_data)) {
signals_.Connect(GTK_WIDGET(user_data), "hide",
G_CALLBACK(OnForeshadowWidgetHideThunk), this);
}
return FALSE;
}
void BubbleGtk::OnForeshadowWidgetHide(GtkWidget* widget) {
if (grab_input_)
GrabPointerAndKeyboard();
signals_.DisconnectAll(widget);
}
gboolean BubbleGtk::OnToplevelConfigure(GtkWidget* widget,
GdkEventConfigure* event) {
if (!UpdateFrameStyle(false))
MoveWindow();
StackWindow();
return FALSE;
}
gboolean BubbleGtk::OnToplevelUnmap(GtkWidget* widget, GdkEvent* event) {
Close();
return FALSE;
}
void BubbleGtk::OnAnchorAllocate(GtkWidget* widget,
GtkAllocation* allocation) {
if (!UpdateFrameStyle(false))
MoveWindow();
}
void BubbleGtk::OnAnchorDestroy(GtkWidget* widget) {
anchor_widget_ = NULL;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&BubbleGtk::Close, weak_ptr_factory_.GetWeakPtr()));
}