summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk/gtk_util.cc
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-02 09:16:44 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-02 09:16:44 +0000
commit16d51df40992e767f4df3cd8a0d50fcf15a20c4c (patch)
treee7d032e5ccf51e42dc95f89c403d1921d70d2b03 /chrome/browser/gtk/gtk_util.cc
parent88301f32ceb483810043b4d426d1a853d00f3035 (diff)
downloadchromium_src-16d51df40992e767f4df3cd8a0d50fcf15a20c4c.zip
chromium_src-16d51df40992e767f4df3cd8a0d50fcf15a20c4c.tar.gz
chromium_src-16d51df40992e767f4df3cd8a0d50fcf15a20c4c.tar.bz2
Next part of bad dependency removal (chrome/common -> chrome/browser)
This change introduces one more dependency on chrome/browser, but it seems simpler to move gtk_util first and then fix it. TEST=none BUG=none Review URL: http://codereview.chromium.org/661271 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40369 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk/gtk_util.cc')
-rw-r--r--chrome/browser/gtk/gtk_util.cc874
1 files changed, 874 insertions, 0 deletions
diff --git a/chrome/browser/gtk/gtk_util.cc b/chrome/browser/gtk/gtk_util.cc
new file mode 100644
index 0000000..465202b
--- /dev/null
+++ b/chrome/browser/gtk/gtk_util.cc
@@ -0,0 +1,874 @@
+// 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 "chrome/browser/gtk/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/browser_list.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/gtk/cairo_cached_surface.h"
+#include "chrome/browser/gtk/gtk_theme_provider.h"
+#include "chrome/common/renderer_preferences.h"
+#include "chrome/common/x11_util.h"
+#include "grit/theme_resources.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+namespace {
+
+const char kBoldLabelMarkup[] = "<span weight='bold'>%s</span>";
+
+// 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;
+}
+
+GtkWidget* LeftAlignMisc(GtkWidget* misc) {
+ gtk_misc_set_alignment(GTK_MISC(misc), 0, 0.5);
+ return misc;
+}
+
+GtkWidget* CreateBoldLabel(const std::string& text) {
+ GtkWidget* label = gtk_label_new(NULL);
+ char* markup = g_markup_printf_escaped(kBoldLabelMarkup, text.c_str());
+ gtk_label_set_markup(GTK_LABEL(label), markup);
+ g_free(markup);
+
+ return LeftAlignMisc(label);
+}
+
+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 {
+ // For a non-resizable window, GTK tries to snap the window size
+ // to the minimum size around the content. We still want to set
+ // the *minimum* window size to allow windows with long titles to
+ // be wide enough to display their titles, but if GTK needs to
+ // make the window *wider* due to very wide controls, we should
+ // allow that too.
+ GdkGeometry geometry;
+ geometry.min_width = width;
+ geometry.min_height = height;
+ gtk_window_set_geometry_hints(window, GTK_WIDGET(window),
+ &geometry, GDK_HINT_MIN_SIZE);
+ }
+ gtk_window_set_resizable(window, resizable ? TRUE : FALSE);
+}
+
+void CenterOverWindow(GtkWindow* window, GtkWindow* parent) {
+ gfx::Rect frame_bounds = gtk_util::GetWidgetScreenBounds(GTK_WIDGET(parent));
+ gfx::Point origin = frame_bounds.origin();
+ gfx::Size size = gtk_util::GetWidgetSize(GTK_WIDGET(window));
+ origin.Offset(
+ (frame_bounds.width() - size.width()) / 2,
+ (frame_bounds.height() - size.height()) / 2);
+
+ // Prevent moving window out of monitor bounds.
+ GdkScreen* screen = gtk_window_get_screen(parent);
+ if (screen) {
+ // It would be better to check against workarea for given monitor
+ // but getting workarea for particular monitor is tricky.
+ gint monitor = gdk_screen_get_monitor_at_window(screen,
+ GTK_WIDGET(parent)->window);
+ GdkRectangle rect;
+ gdk_screen_get_monitor_geometry(screen, monitor, &rect);
+
+ // Check the right bottom corner.
+ if (origin.x() > rect.x + rect.width - size.width())
+ origin.set_x(rect.x + rect.width - size.width());
+ if (origin.y() > rect.y + rect.height - size.height())
+ origin.set_y(rect.y + rect.height - size.height());
+
+ // Check the left top corner.
+ if (origin.x() < rect.x)
+ origin.set_x(rect.x);
+ if (origin.y() < rect.y)
+ origin.set_y(rect.y);
+ }
+
+ gtk_window_move(window, origin.x(), origin.y());
+
+ // Move to user expected desktop if window is already visible.
+ if (GTK_WIDGET(window)->window) {
+ x11_util::ChangeWindowDesktop(
+ x11_util::GetX11WindowFromGtkWidget(GTK_WIDGET(window)),
+ x11_util::GetX11WindowFromGtkWidget(GTK_WIDGET(parent)));
+ }
+}
+
+void MakeAppModalWindowGroup() {
+#if GTK_CHECK_VERSION(2, 14, 0)
+ // Older versions of GTK+ don't give us gtk_window_group_list() which is what
+ // we need to add current non-browser modal dialogs to the list. If
+ // we have 2.14+ we can do things the correct way.
+ GtkWindowGroup* window_group = gtk_window_group_new();
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end(); ++it) {
+ // List all windows in this current group
+ GtkWindowGroup* old_group =
+ gtk_window_get_group((*it)->window()->GetNativeHandle());
+
+ GList* all_windows = gtk_window_group_list_windows(old_group);
+ for (GList* window = all_windows; window; window = window->next) {
+ gtk_window_group_add_window(window_group, GTK_WINDOW(window->data));
+ }
+ g_list_free(all_windows);
+ }
+ g_object_unref(window_group);
+#else
+ // Otherwise just grab all browser windows and be slightly broken.
+ GtkWindowGroup* window_group = gtk_window_group_new();
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end(); ++it) {
+ gtk_window_group_add_window(window_group,
+ (*it)->window()->GetNativeHandle());
+ }
+ g_object_unref(window_group);
+#endif
+}
+
+void AppModalDismissedUngroupWindows() {
+#if GTK_CHECK_VERSION(2, 14, 0)
+ if (BrowserList::begin() != BrowserList::end()) {
+ std::vector<GtkWindow*> transient_windows;
+
+ // All windows should be part of one big modal group right now.
+ GtkWindowGroup* window_group = gtk_window_get_group(
+ (*BrowserList::begin())->window()->GetNativeHandle());
+ GList* windows = gtk_window_group_list_windows(window_group);
+
+ for (GList* item = windows; item; item = item->next) {
+ GtkWindow* window = GTK_WINDOW(item->data);
+ GtkWindow* transient_for = gtk_window_get_transient_for(window);
+ if (transient_for) {
+ transient_windows.push_back(window);
+ } else {
+ GtkWindowGroup* window_group = gtk_window_group_new();
+ gtk_window_group_add_window(window_group, window);
+ g_object_unref(window_group);
+ }
+ }
+
+ // Put each transient window in the same group as its transient parent.
+ for (std::vector<GtkWindow*>::iterator it = transient_windows.begin();
+ it != transient_windows.end(); ++it) {
+ GtkWindow* transient_parent = gtk_window_get_transient_for(*it);
+ GtkWindowGroup* group = gtk_window_get_group(transient_parent);
+ gtk_window_group_add_window(group, *it);
+ }
+ }
+#else
+ // This is slightly broken in the case where a different window had a dialog,
+ // but its the best we can do since we don't have newer gtk stuff.
+ for (BrowserList::const_iterator it = BrowserList::begin();
+ it != BrowserList::end(); ++it) {
+ GtkWindowGroup* window_group = gtk_window_group_new();
+ gtk_window_group_add_window(window_group,
+ (*it)->window()->GetNativeHandle());
+ g_object_unref(window_group);
+ }
+#endif
+}
+
+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);
+}
+
+gfx::Size GetWidgetSize(GtkWidget* widget) {
+ GtkRequisition size;
+ gtk_widget_size_request(widget, &size);
+ return gfx::Size(size.width, size.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(button, "button-press-event",
+ G_CALLBACK(OnMouseButtonPressed), userdata);
+ g_signal_connect(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);
+
+ // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
+ // the default value for gtk-cursor-blink-time.
+ static const gint kGtkDefaultCursorBlinkTime = 1200;
+
+ gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
+ gboolean cursor_blink = TRUE;
+ gint antialias = 0;
+ gint hinting = 0;
+ gchar* hint_style = NULL;
+ gchar* rgba_style = NULL;
+ g_object_get(gtk_settings_get_default(),
+ "gtk-cursor-blink-time", &cursor_blink_time,
+ "gtk-cursor-blink", &cursor_blink,
+ "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;
+
+ if (cursor_blink) {
+ // Dividing by 2*1000ms follows the WebKit GTK port and makes the blink
+ // frequency appear similar to the omnibox. Without this the blink is too
+ // slow.
+ prefs->caret_blink_interval = cursor_blink_time / 2000.;
+ } else {
+ prefs->caret_blink_interval = 0;
+ }
+
+ // 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);
+}
+
+WindowOpenDisposition DispositionForCurrentButtonPressEvent() {
+ GdkEvent* event = gtk_get_current_event();
+ if (!event) {
+ NOTREACHED();
+ return NEW_FOREGROUND_TAB;
+ }
+
+ guint state = event->button.state;
+ gdk_event_free(event);
+ return event_utils::DispositionFromEventFlags(state);
+}
+
+} // namespace gtk_util