// 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 #include #include #include "base/linux_util.h" #include "base/logging.h" #include "third_party/skia/include/core/SkBitmap.h" namespace { // Callback used in RemoveAllChildren. void RemoveWidget(GtkWidget* widget, gpointer container) { gtk_container_remove(GTK_CONTAINER(container), widget); } } // 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(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); 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 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) { int x = 0, y = 0; GtkWidget* parent = widget; while (parent) { if (GTK_IS_WINDOW(parent)) { int window_x, window_y; // Returns the origin of the window, excluding the frame if one is exists. gdk_window_get_origin(parent->window, &window_x, &window_y); x += window_x; y += window_y; return gfx::Point(x, y); } x += parent->allocation.x; y += parent->allocation.y; parent = gtk_widget_get_parent(parent); } 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()); for (size_t i = 0; i < label.length(); ++i) { 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 EnumerateChildWindows(EnumerateWindowsDelegate* delegate) { GdkScreen* screen = gdk_screen_get_default(); GList* stack = gdk_screen_get_window_stack(screen); DCHECK(stack); for (GList* iter = g_list_last(stack); iter; iter = iter->prev) { GdkWindow* window = static_cast(iter->data); XID xid = GDK_WINDOW_XID(window); if (delegate->ShouldStopIterating(xid)) break; } g_list_foreach(stack, (GFunc)g_object_unref, NULL); g_list_free(stack); } } // namespace gtk_util