blob: 37fb13e9165d48abcba6e316c624678e8969a172 [file] [log] [blame]
// Copyright (c) 2009 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 "chrome/common/gtk_util.h"
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <cstdarg>
#include <map>
#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "base/linux_util.h"
#include "base/logging.h"
#include "chrome/browser/gtk/cairo_cached_surface.h"
#include "chrome/browser/gtk/gtk_theme_provider.h"
#include "chrome/common/renderer_preferences.h"
#include "grit/theme_resources.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColor.h"
namespace {
// Callback used in RemoveAllChildren.
void RemoveWidget(GtkWidget* widget, gpointer container) {
gtk_container_remove(GTK_CONTAINER(container), widget);
}
// These two functions are copped almost directly from gtk core. The only
// difference is that they accept middle clicks.
gboolean OnMouseButtonPressed(GtkWidget* widget, GdkEventButton* event,
gpointer userdata) {
if (event->type == GDK_BUTTON_PRESS) {
if (gtk_button_get_focus_on_click(GTK_BUTTON(widget)) &&
!GTK_WIDGET_HAS_FOCUS(widget)) {
gtk_widget_grab_focus(widget);
}
gint button_mask = GPOINTER_TO_INT(userdata);
if (button_mask && (1 << event->button))
gtk_button_pressed(GTK_BUTTON(widget));
}
return TRUE;
}
gboolean OnMouseButtonReleased(GtkWidget* widget, GdkEventButton* event,
gpointer userdata) {
gint button_mask = GPOINTER_TO_INT(userdata);
if (button_mask && (1 << event->button))
gtk_button_released(GTK_BUTTON(widget));
return TRUE;
}
// Ownership of |icon_list| is passed to the caller.
GList* GetIconList() {
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
GList* icon_list = NULL;
icon_list = g_list_append(icon_list, rb.GetPixbufNamed(IDR_PRODUCT_ICON_32));
icon_list = g_list_append(icon_list, rb.GetPixbufNamed(IDR_PRODUCT_LOGO_16));
return icon_list;
}
// A process wide singleton that manages our usage of gdk
// cursors. gdk_cursor_new() hits the disk in several places and GdkCursor
// instances can be reused throughout the process.
class GdkCursorCache {
public:
~GdkCursorCache() {
for (std::map<GdkCursorType, GdkCursor*>::iterator it =
cursor_cache_.begin(); it != cursor_cache_.end(); ++it) {
gdk_cursor_unref(it->second);
}
cursor_cache_.clear();
}
GdkCursor* GetCursorImpl(GdkCursorType type) {
std::map<GdkCursorType, GdkCursor*>::iterator it = cursor_cache_.find(type);
GdkCursor* cursor = NULL;
if (it == cursor_cache_.end()) {
cursor = gdk_cursor_new(type);
cursor_cache_.insert(std::make_pair(type, cursor));
} else {
cursor = it->second;
}
// Add a reference to the returned cursor because our consumers mix us with
// gdk_cursor_new(). Both the normal constructor and GetCursorImpls() need
// to be paired with a gdk_cursor_unref() so ref it here (as we own the ref
// that comes from gdk_cursor_new().
gdk_cursor_ref(cursor);
return cursor;
}
std::map<GdkCursorType, GdkCursor*> cursor_cache_;
};
// Expose event handler for a container that simply suppresses the default
// drawing and propagates the expose event to the container's children.
gboolean PaintNoBackground(GtkWidget* widget,
GdkEventExpose* event,
gpointer unused) {
GList* children = gtk_container_get_children(GTK_CONTAINER(widget));
for (GList* item = children; item; item = item->next) {
gtk_container_propagate_expose(GTK_CONTAINER(widget),
GTK_WIDGET(item->data),
event);
}
g_list_free(children);
return TRUE;
}
void OnLabelAllocate(GtkWidget* label, GtkAllocation* allocation,
gpointer user_data) {
gtk_widget_set_size_request(label, allocation->width, -1);
}
} // namespace
namespace event_utils {
WindowOpenDisposition DispositionFromEventFlags(guint event_flags) {
if ((event_flags & GDK_BUTTON2_MASK) || (event_flags & GDK_CONTROL_MASK)) {
return (event_flags & GDK_SHIFT_MASK) ?
NEW_FOREGROUND_TAB : NEW_BACKGROUND_TAB;
}
if (event_flags & GDK_SHIFT_MASK)
return NEW_WINDOW;
return false /*event.IsAltDown()*/ ? SAVE_TO_DISK : CURRENT_TAB;
}
} // namespace event_utils
namespace gtk_util {
GtkWidget* CreateLabeledControlsGroup(std::vector<GtkWidget*>* labels,
const char* text, ...) {
va_list ap;
va_start(ap, text);
GtkWidget* table = gtk_table_new(0, 2, FALSE);
gtk_table_set_col_spacing(GTK_TABLE(table), 0, kLabelSpacing);
gtk_table_set_row_spacings(GTK_TABLE(table), kControlSpacing);
for (guint row = 0; text; ++row) {
gtk_table_resize(GTK_TABLE(table), row + 1, 2);
GtkWidget* control = va_arg(ap, GtkWidget*);
GtkWidget* label = gtk_label_new(text);
gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
if (labels)
labels->push_back(label);
gtk_table_attach(GTK_TABLE(table), label,
0, 1, row, row + 1,
GTK_FILL, GTK_FILL,
0, 0);
gtk_table_attach_defaults(GTK_TABLE(table), control,
1, 2, row, row + 1);
text = va_arg(ap, const char*);
}
va_end(ap);
return table;
}
GtkWidget* CreateGtkBorderBin(GtkWidget* child, const GdkColor* color,
int top, int bottom, int left, int right) {
// Use a GtkEventBox to get the background painted. However, we can't just
// use a container border, since it won't paint there. Use an alignment
// inside to get the sizes exactly of how we want the border painted.
GtkWidget* ebox = gtk_event_box_new();
if (color)
gtk_widget_modify_bg(ebox, GTK_STATE_NORMAL, color);
GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), top, bottom, left, right);
gtk_container_add(GTK_CONTAINER(alignment), child);
gtk_container_add(GTK_CONTAINER(ebox), alignment);
return ebox;
}
void GetWidgetSizeFromResources(GtkWidget* widget, int width_chars,
int height_lines, int* width, int* height) {
DCHECK(GTK_WIDGET_REALIZED(widget))
<< " widget must be realized to compute font metrics correctly";
double chars = 0;
if (width)
StringToDouble(l10n_util::GetStringUTF8(width_chars), &chars);
double lines = 0;
if (height)
StringToDouble(l10n_util::GetStringUTF8(height_lines), &lines);
GetWidgetSizeFromCharacters(widget, chars, lines, width, height);
}
void GetWidgetSizeFromCharacters(GtkWidget* widget, double width_chars,
double height_lines, int* width, int* height) {
DCHECK(GTK_WIDGET_REALIZED(widget))
<< " widget must be realized to compute font metrics correctly";
PangoContext* context = gtk_widget_create_pango_context(widget);
PangoFontMetrics* metrics = pango_context_get_metrics(context,
widget->style->font_desc, pango_context_get_language(context));
if (width) {
*width = static_cast<int>(
pango_font_metrics_get_approximate_char_width(metrics) *
width_chars / PANGO_SCALE);
}
if (height) {
*height = static_cast<int>(
(pango_font_metrics_get_ascent(metrics) +
pango_font_metrics_get_descent(metrics)) *
height_lines / PANGO_SCALE);
}
pango_font_metrics_unref(metrics);
g_object_unref(context);
}
void SetWindowSizeFromResources(GtkWindow* window,
int width_id, int height_id, bool resizable) {
int width = -1;
int height = -1;
gtk_util::GetWidgetSizeFromResources(GTK_WIDGET(window), width_id, height_id,
(width_id != -1) ? &width : NULL,
(height_id != -1) ? &height : NULL);
if (resizable) {
gtk_window_set_default_size(window, width, height);
} else {
gtk_widget_set_size_request(GTK_WIDGET(window), width, height);
}
gtk_window_set_resizable(window, resizable ? TRUE : FALSE);
}
void RemoveAllChildren(GtkWidget* container) {
gtk_container_foreach(GTK_CONTAINER(container), RemoveWidget, container);
}
void ForceFontSizePixels(GtkWidget* widget, double size_pixels) {
GtkStyle* style = widget->style;
PangoFontDescription* font_desc = style->font_desc;
// pango_font_description_set_absolute_size sets the font size in device
// units, which for us is pixels.
pango_font_description_set_absolute_size(font_desc,
PANGO_SCALE * size_pixels);
gtk_widget_modify_font(widget, font_desc);
}
gfx::Point GetWidgetScreenPosition(GtkWidget* widget) {
if (!widget->window) {
NOTREACHED() << "Must only be called on realized widgets.";
return gfx::Point(0, 0);
}
gint x, y;
gdk_window_get_origin(widget->window, &x, &y);
if (!GTK_IS_WINDOW(widget)) {
x += widget->allocation.x;
y += widget->allocation.y;
}
return gfx::Point(x, y);
}
gfx::Rect GetWidgetScreenBounds(GtkWidget* widget) {
gfx::Point position = GetWidgetScreenPosition(widget);
return gfx::Rect(position.x(), position.y(),
widget->allocation.width, widget->allocation.height);
}
void ConvertWidgetPointToScreen(GtkWidget* widget, gfx::Point* p) {
DCHECK(widget);
DCHECK(p);
gfx::Point position = GetWidgetScreenPosition(widget);
p->SetPoint(p->x() + position.x(), p->y() + position.y());
}
void InitRCStyles() {
static const char kRCText[] =
// Make our dialogs styled like the GNOME HIG.
//
// TODO(evanm): content-area-spacing was introduced in a later
// version of GTK, so we need to set that manually on all dialogs.
// Perhaps it would make sense to have a shared FixupDialog() function.
"style \"gnome-dialog\" {\n"
" xthickness = 12\n"
" GtkDialog::action-area-border = 0\n"
" GtkDialog::button-spacing = 6\n"
" GtkDialog::content-area-spacing = 18\n"
" GtkDialog::content-area-border = 12\n"
"}\n"
// Note we set it at the "application" priority, so users can override.
"widget \"GtkDialog\" style : application \"gnome-dialog\"\n"
// Make our about dialog special, so the image is flush with the edge.
"style \"about-dialog\" {\n"
" GtkDialog::action-area-border = 12\n"
" GtkDialog::button-spacing = 6\n"
" GtkDialog::content-area-spacing = 18\n"
" GtkDialog::content-area-border = 0\n"
"}\n"
"widget \"about-dialog\" style : application \"about-dialog\"\n";
gtk_rc_parse_string(kRCText);
}
void CenterWidgetInHBox(GtkWidget* hbox, GtkWidget* widget, bool pack_at_end,
int padding) {
GtkWidget* centering_vbox = gtk_vbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(centering_vbox), widget, TRUE, FALSE, 0);
if (pack_at_end)
gtk_box_pack_end(GTK_BOX(hbox), centering_vbox, FALSE, FALSE, padding);
else
gtk_box_pack_start(GTK_BOX(hbox), centering_vbox, FALSE, FALSE, padding);
}
std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
std::string ret;
ret.reserve(label.length() * 2);
for (size_t i = 0; i < label.length(); ++i) {
if ('_' == label[i]) {
ret.push_back('_');
ret.push_back('_');
} else if ('&' == label[i]) {
if (i + 1 < label.length() && '&' == label[i + 1]) {
ret.push_back(label[i]);
++i;
} else {
ret.push_back('_');
}
} else {
ret.push_back(label[i]);
}
}
return ret;
}
bool IsScreenComposited() {
GdkScreen* screen = gdk_screen_get_default();
return gdk_screen_is_composited(screen) == TRUE;
}
void EnumerateTopLevelWindows(x11_util::EnumerateWindowsDelegate* delegate) {
std::vector<XID> stack;
if (!x11_util::GetXWindowStack(&stack)) {
// Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
// to old school enumeration of all X windows. Some WMs parent 'top-level'
// windows in unnamed actual top-level windows (ion WM), so extend the
// search depth to all children of top-level windows.
const int kMaxSearchDepth = 1;
x11_util::EnumerateAllWindows(delegate, kMaxSearchDepth);
return;
}
std::vector<XID>::iterator iter;
for (iter = stack.begin(); iter != stack.end(); iter++) {
if (delegate->ShouldStopIterating(*iter))
return;
}
}
void SetButtonClickableByMouseButtons(GtkWidget* button,
bool left, bool middle, bool right) {
gint button_mask = 0;
if (left)
button_mask |= 1 << 1;
if (middle)
button_mask |= 1 << 2;
if (right)
button_mask |= 1 << 3;
void* userdata = GINT_TO_POINTER(button_mask);
g_signal_connect(G_OBJECT(button), "button-press-event",
G_CALLBACK(OnMouseButtonPressed), userdata);
g_signal_connect(G_OBJECT(button), "button-release-event",
G_CALLBACK(OnMouseButtonReleased), userdata);
}
void SetButtonTriggersNavigation(GtkWidget* button) {
SetButtonClickableByMouseButtons(button, true, true, false);
}
int MirroredLeftPointForRect(GtkWidget* widget, const gfx::Rect& bounds) {
if (l10n_util::GetTextDirection() != l10n_util::RIGHT_TO_LEFT) {
return bounds.x();
}
return widget->allocation.width - bounds.x() - bounds.width();
}
int MirroredXCoordinate(GtkWidget* widget, int x) {
if (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) {
return widget->allocation.width - x;
}
return x;
}
bool WidgetContainsCursor(GtkWidget* widget) {
gint x = 0;
gint y = 0;
gtk_widget_get_pointer(widget, &x, &y);
// To quote the gtk docs:
//
// Widget coordinates are a bit odd; for historical reasons, they are
// defined as widget->window coordinates for widgets that are not
// GTK_NO_WINDOW widgets, and are relative to widget->allocation.x,
// widget->allocation.y for widgets that are GTK_NO_WINDOW widgets.
//
// So the base is always (0,0).
gfx::Rect widget_allocation(0, 0, widget->allocation.width,
widget->allocation.height);
return widget_allocation.Contains(x, y);
}
void SetWindowIcon(GtkWindow* window) {
GList* icon_list = GetIconList();
gtk_window_set_icon_list(window, icon_list);
g_list_free(icon_list);
}
void SetDefaultWindowIcon() {
GList* icon_list = GetIconList();
gtk_window_set_default_icon_list(icon_list);
g_list_free(icon_list);
}
GtkWidget* AddButtonToDialog(GtkWidget* dialog, const gchar* text,
const gchar* stock_id, gint response_id) {
GtkWidget* button = gtk_button_new_with_label(text);
gtk_button_set_image(GTK_BUTTON(button),
gtk_image_new_from_stock(stock_id,
GTK_ICON_SIZE_BUTTON));
gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button,
response_id);
return button;
}
void SetLabelColor(GtkWidget* label, const GdkColor* color) {
gtk_widget_modify_fg(label, GTK_STATE_NORMAL, color);
gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, color);
gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, color);
gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, color);
}
GtkWidget* IndentWidget(GtkWidget* content) {
GtkWidget* content_alignment = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
gtk_alignment_set_padding(GTK_ALIGNMENT(content_alignment), 0, 0,
gtk_util::kGroupIndent, 0);
gtk_container_add(GTK_CONTAINER(content_alignment), content);
return content_alignment;
}
void UpdateGtkFontSettings(RendererPreferences* prefs) {
DCHECK(prefs);
gint antialias = 0;
gint hinting = 0;
gchar* hint_style = NULL;
gchar* rgba_style = NULL;
g_object_get(gtk_settings_get_default(),
"gtk-xft-antialias", &antialias,
"gtk-xft-hinting", &hinting,
"gtk-xft-hintstyle", &hint_style,
"gtk-xft-rgba", &rgba_style,
NULL);
// Set some reasonable defaults.
prefs->should_antialias_text = true;
prefs->hinting = RENDERER_PREFERENCES_HINTING_SYSTEM_DEFAULT;
prefs->subpixel_rendering =
RENDERER_PREFERENCES_SUBPIXEL_RENDERING_SYSTEM_DEFAULT;
// g_object_get() doesn't tell us whether the properties were present or not,
// but if they aren't (because gnome-settings-daemon isn't running), we'll get
// NULL values for the strings.
if (hint_style && rgba_style) {
prefs->should_antialias_text = antialias;
if (hinting == 0 || strcmp(hint_style, "hintnone") == 0) {
prefs->hinting = RENDERER_PREFERENCES_HINTING_NONE;
} else if (strcmp(hint_style, "hintslight") == 0) {
prefs->hinting = RENDERER_PREFERENCES_HINTING_SLIGHT;
} else if (strcmp(hint_style, "hintmedium") == 0) {
prefs->hinting = RENDERER_PREFERENCES_HINTING_MEDIUM;
} else if (strcmp(hint_style, "hintfull") == 0) {
prefs->hinting = RENDERER_PREFERENCES_HINTING_FULL;
}
if (strcmp(rgba_style, "none") == 0) {
prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_NONE;
} else if (strcmp(rgba_style, "rgb") == 0) {
prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_RGB;
} else if (strcmp(rgba_style, "bgr") == 0) {
prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_BGR;
} else if (strcmp(rgba_style, "vrgb") == 0) {
prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VRGB;
} else if (strcmp(rgba_style, "vbgr") == 0) {
prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VBGR;
}
}
if (hint_style)
g_free(hint_style);
if (rgba_style)
g_free(rgba_style);
}
gfx::Point ScreenPoint(GtkWidget* widget) {
int x, y;
gdk_display_get_pointer(gtk_widget_get_display(widget), NULL, &x, &y,
NULL);
return gfx::Point(x, y);
}
gfx::Point ClientPoint(GtkWidget* widget) {
int x, y;
gtk_widget_get_pointer(widget, &x, &y);
return gfx::Point(x, y);
}
GdkPoint MakeBidiGdkPoint(gint x, gint y, gint width, bool ltr) {
GdkPoint point = {ltr ? x : width - x, y};
return point;
}
void DrawTextEntryBackground(GtkWidget* offscreen_entry,
GtkWidget* widget_to_draw_on,
GdkRectangle* dirty_rec,
GdkRectangle* rec) {
GtkStyle* gtk_owned_style = gtk_rc_get_style(offscreen_entry);
// GTK owns the above and we're going to have to make our own copy of it
// that we can edit.
GtkStyle* our_style = gtk_style_copy(gtk_owned_style);
our_style = gtk_style_attach(our_style, widget_to_draw_on->window);
// TODO(erg): Draw the focus ring if appropriate...
// We're using GTK rendering; draw a GTK entry widget onto the background.
gtk_paint_shadow(our_style, widget_to_draw_on->window,
GTK_STATE_NORMAL, GTK_SHADOW_IN, dirty_rec,
widget_to_draw_on, "entry",
rec->x, rec->y, rec->width, rec->height);
// Draw the interior background (not all themes draw the entry background
// above; this is a noop on themes that do).
gint xborder = our_style->xthickness;
gint yborder = our_style->ythickness;
gtk_paint_flat_box(our_style, widget_to_draw_on->window,
GTK_STATE_NORMAL, GTK_SHADOW_NONE, dirty_rec,
widget_to_draw_on, "entry_bg",
rec->x + xborder, rec->y + yborder,
rec->width - 2 * xborder,
rec->height - 2 * yborder);
g_object_unref(our_style);
}
void DrawThemedToolbarBackground(GtkWidget* widget,
cairo_t* cr,
GdkEventExpose* event,
const gfx::Point& tabstrip_origin,
GtkThemeProvider* theme_provider) {
// Fill the entire region with the toolbar color.
GdkColor color = theme_provider->GetGdkColor(
BrowserThemeProvider::COLOR_TOOLBAR);
gdk_cairo_set_source_color(cr, &color);
cairo_fill(cr);
// The toolbar is supposed to blend in with the active tab, so we have to pass
// coordinates for the IDR_THEME_TOOLBAR bitmap relative to the top of the
// tab strip.
CairoCachedSurface* background = theme_provider->GetSurfaceNamed(
IDR_THEME_TOOLBAR, widget);
background->SetSource(cr, tabstrip_origin.x(), tabstrip_origin.y());
// We tile the toolbar background in both directions.
cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
cairo_rectangle(cr,
tabstrip_origin.x(),
tabstrip_origin.y(),
event->area.x + event->area.width - tabstrip_origin.x(),
event->area.y + event->area.height - tabstrip_origin.y());
cairo_fill(cr);
}
GdkColor AverageColors(GdkColor color_one, GdkColor color_two) {
GdkColor average_color;
average_color.pixel = 0;
average_color.red = (color_one.red + color_two.red) / 2;
average_color.green = (color_one.green + color_two.green) / 2;
average_color.blue = (color_one.blue + color_two.blue) / 2;
return average_color;
}
void SetAlwaysShowImage(GtkWidget* image_menu_item) {
// Compile time check: if it's available, just use the API.
// GTK_CHECK_VERSION is TRUE if the passed version is compatible.
#if GTK_CHECK_VERSION(2, 16, 1)
gtk_image_menu_item_set_always_show_image(
GTK_IMAGE_MENU_ITEM(image_menu_item), TRUE);
#else
// Run time check: if the API is not available, set the property manually.
// This will still only work with GTK 2.16+ as the property doesn't exist
// in earlier versions.
// gtk_check_version() returns NULL if the passed version is compatible.
if (!gtk_check_version(2, 16, 1)) {
GValue true_value = { 0 };
g_value_init(&true_value, G_TYPE_BOOLEAN);
g_value_set_boolean(&true_value, TRUE);
g_object_set_property(G_OBJECT(image_menu_item), "always-show-image",
&true_value);
}
#endif
}
GdkCursor* GetCursor(GdkCursorType type) {
static GdkCursorCache impl;
return impl.GetCursorImpl(type);
}
void StackPopupWindow(GtkWidget* popup, GtkWidget* toplevel) {
DCHECK(GTK_IS_WINDOW(popup) && GTK_WIDGET_TOPLEVEL(popup) &&
GTK_WIDGET_REALIZED(popup));
DCHECK(GTK_IS_WINDOW(toplevel) && GTK_WIDGET_TOPLEVEL(toplevel) &&
GTK_WIDGET_REALIZED(toplevel));
// Stack the |popup| window directly above the |toplevel| window.
// The popup window is a direct child of the root window, so we need to
// find a similar ancestor for the toplevel window (which might have been
// reparented by a window manager). We grab the server while we're doing
// this -- otherwise, we'll get an error if the window manager reparents the
// toplevel window right after we call GetHighestAncestorWindow().
gdk_x11_display_grab(gtk_widget_get_display(toplevel));
XID toplevel_window_base = x11_util::GetHighestAncestorWindow(
x11_util::GetX11WindowFromGtkWidget(toplevel),
x11_util::GetX11RootWindow());
if (toplevel_window_base) {
XID window_xid = x11_util::GetX11WindowFromGtkWidget(popup);
XID window_parent = x11_util::GetParentWindow(window_xid);
if (window_parent == x11_util::GetX11RootWindow()) {
x11_util::RestackWindow(window_xid, toplevel_window_base, true);
} else {
// The window manager shouldn't reparent override-redirect windows.
DLOG(ERROR) << "override-redirect window " << window_xid
<< "'s parent is " << window_parent
<< ", rather than root window "
<< x11_util::GetX11RootWindow();
}
}
gdk_x11_display_ungrab(gtk_widget_get_display(toplevel));
}
gfx::Rect GetWidgetRectRelativeToToplevel(GtkWidget* widget) {
DCHECK(GTK_WIDGET_REALIZED(widget));
GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
DCHECK(toplevel);
DCHECK(GTK_WIDGET_REALIZED(toplevel));
gint x = 0, y = 0;
gtk_widget_translate_coordinates(widget,
toplevel,
0, 0,
&x, &y);
return gfx::Rect(x, y, widget->allocation.width, widget->allocation.height);
}
void ApplyMessageDialogQuirks(GtkWidget* dialog) {
if (gtk_window_get_modal(GTK_WINDOW(dialog))) {
// Work around a KDE 3 window manager bug.
scoped_ptr<base::EnvironmentVariableGetter> env(
base::EnvironmentVariableGetter::Create());
if (base::DESKTOP_ENVIRONMENT_KDE3 == GetDesktopEnvironment(env.get()))
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
}
}
void SuppressDefaultPainting(GtkWidget* container) {
g_signal_connect(container, "expose-event",
G_CALLBACK(PaintNoBackground), NULL);
}
void WrapLabelAtAllocationHack(GtkWidget* label) {
g_signal_connect(label, "size-allocate",
G_CALLBACK(OnLabelAllocate), NULL);
}
} // namespace gtk_util