Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/gtk_util.h"
      6 
      7 #include <cairo/cairo.h>
      8 #include <gdk/gdkx.h>
      9 #include <gtk/gtk.h>
     10 
     11 #include <cstdarg>
     12 #include <map>
     13 
     14 #include "base/environment.h"
     15 #include "base/i18n/rtl.h"
     16 #include "base/linux_util.h"
     17 #include "base/logging.h"
     18 #include "base/nix/xdg_util.h"
     19 #include "base/string_number_conversions.h"
     20 #include "base/utf_string_conversions.h"
     21 #include "chrome/browser/autocomplete/autocomplete.h"
     22 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
     23 #include "chrome/browser/autocomplete/autocomplete_match.h"
     24 #include "chrome/browser/profiles/profile.h"
     25 #include "chrome/browser/ui/browser_list.h"
     26 #include "chrome/browser/ui/browser_window.h"
     27 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
     28 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
     29 #include "content/browser/disposition_utils.h"
     30 #include "content/browser/renderer_host/render_view_host.h"
     31 #include "content/browser/tab_contents/tab_contents.h"
     32 #include "content/common/renderer_preferences.h"
     33 #include "googleurl/src/gurl.h"
     34 #include "grit/theme_resources.h"
     35 #include "third_party/skia/include/core/SkBitmap.h"
     36 #include "third_party/skia/include/core/SkColor.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 #include "ui/base/resource/resource_bundle.h"
     39 #include "ui/base/x/x11_util.h"
     40 #include "ui/gfx/gtk_util.h"
     41 
     42 #if defined(OS_CHROMEOS)
     43 #include "chrome/browser/chromeos/frame/browser_view.h"
     44 #include "chrome/browser/chromeos/native_dialog_window.h"
     45 #include "views/window/window.h"
     46 #else
     47 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
     48 #endif
     49 
     50 using WebKit::WebDragOperationsMask;
     51 using WebKit::WebDragOperation;
     52 using WebKit::WebDragOperationNone;
     53 using WebKit::WebDragOperationCopy;
     54 using WebKit::WebDragOperationLink;
     55 using WebKit::WebDragOperationMove;
     56 
     57 namespace {
     58 
     59 #if defined(GOOGLE_CHROME_BUILD)
     60 static const char* kIconName = "google-chrome";
     61 #else
     62 static const char* kIconName = "chromium-browser";
     63 #endif
     64 
     65 const char kBoldLabelMarkup[] = "<span weight='bold'>%s</span>";
     66 
     67 // Callback used in RemoveAllChildren.
     68 void RemoveWidget(GtkWidget* widget, gpointer container) {
     69   gtk_container_remove(GTK_CONTAINER(container), widget);
     70 }
     71 
     72 // These two functions are copped almost directly from gtk core. The only
     73 // difference is that they accept middle clicks.
     74 gboolean OnMouseButtonPressed(GtkWidget* widget, GdkEventButton* event,
     75                               gpointer userdata) {
     76   if (event->type == GDK_BUTTON_PRESS) {
     77     if (gtk_button_get_focus_on_click(GTK_BUTTON(widget)) &&
     78         !GTK_WIDGET_HAS_FOCUS(widget)) {
     79       gtk_widget_grab_focus(widget);
     80     }
     81 
     82     gint button_mask = GPOINTER_TO_INT(userdata);
     83     if (button_mask & (1 << event->button))
     84       gtk_button_pressed(GTK_BUTTON(widget));
     85   }
     86 
     87   return TRUE;
     88 }
     89 
     90 gboolean OnMouseButtonReleased(GtkWidget* widget, GdkEventButton* event,
     91                                gpointer userdata) {
     92   gint button_mask = GPOINTER_TO_INT(userdata);
     93   if (button_mask && (1 << event->button))
     94     gtk_button_released(GTK_BUTTON(widget));
     95 
     96   return TRUE;
     97 }
     98 
     99 // Returns the approximate number of characters that can horizontally fit in
    100 // |pixel_width| pixels.
    101 int GetCharacterWidthForPixels(GtkWidget* widget, int pixel_width) {
    102   DCHECK(GTK_WIDGET_REALIZED(widget))
    103       << " widget must be realized to compute font metrics correctly";
    104 
    105   PangoContext* context = gtk_widget_create_pango_context(widget);
    106   PangoFontMetrics* metrics = pango_context_get_metrics(context,
    107       widget->style->font_desc, pango_context_get_language(context));
    108 
    109   // This technique (max of char and digit widths) matches the code in
    110   // gtklabel.c.
    111   int char_width = pixel_width * PANGO_SCALE /
    112       std::max(pango_font_metrics_get_approximate_char_width(metrics),
    113                pango_font_metrics_get_approximate_digit_width(metrics));
    114 
    115   pango_font_metrics_unref(metrics);
    116   g_object_unref(context);
    117 
    118   return char_width;
    119 }
    120 
    121 void OnLabelRealize(GtkWidget* label, gpointer pixel_width) {
    122   gtk_label_set_width_chars(
    123       GTK_LABEL(label),
    124       GetCharacterWidthForPixels(label,GPOINTER_TO_INT(pixel_width)));
    125 }
    126 
    127 // Ownership of |icon_list| is passed to the caller.
    128 GList* GetIconList() {
    129   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    130   GList* icon_list = NULL;
    131   icon_list = g_list_append(icon_list, rb.GetPixbufNamed(IDR_PRODUCT_ICON_32));
    132   icon_list = g_list_append(icon_list, rb.GetPixbufNamed(IDR_PRODUCT_LOGO_16));
    133   return icon_list;
    134 }
    135 
    136 // Expose event handler for a container that simply suppresses the default
    137 // drawing and propagates the expose event to the container's children.
    138 gboolean PaintNoBackground(GtkWidget* widget,
    139                            GdkEventExpose* event,
    140                            gpointer unused) {
    141   GList* children = gtk_container_get_children(GTK_CONTAINER(widget));
    142   for (GList* item = children; item; item = item->next) {
    143     gtk_container_propagate_expose(GTK_CONTAINER(widget),
    144                                    GTK_WIDGET(item->data),
    145                                    event);
    146   }
    147   g_list_free(children);
    148 
    149   return TRUE;
    150 }
    151 
    152 #if defined(OS_CHROMEOS)
    153 
    154 TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
    155   chromeos::BrowserView* browser_view = static_cast<chromeos::BrowserView*>(
    156       window);
    157   return browser_view->GetSelectedTabContents();
    158 }
    159 
    160 GtkWidget* GetBrowserWindowFocusedWidget(BrowserWindow* window) {
    161   gfx::NativeView widget = gtk_window_get_focus(window->GetNativeHandle());
    162 
    163   if (widget == NULL) {
    164     chromeos::BrowserView* browser_view = static_cast<chromeos::BrowserView*>(
    165         window);
    166     widget = browser_view->saved_focused_widget();
    167   }
    168 
    169   return widget;
    170 }
    171 
    172 #else
    173 
    174 TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
    175   BrowserWindowGtk* browser_window = static_cast<BrowserWindowGtk*>(
    176       window);
    177   return browser_window->browser()->GetSelectedTabContents();
    178 }
    179 
    180 GtkWidget* GetBrowserWindowFocusedWidget(BrowserWindow* window) {
    181   return gtk_window_get_focus(window->GetNativeHandle());
    182 }
    183 
    184 #endif
    185 
    186 }  // namespace
    187 
    188 namespace event_utils {
    189 
    190 WindowOpenDisposition DispositionFromEventFlags(guint event_flags) {
    191   return disposition_utils::DispositionFromClick(
    192       event_flags & GDK_BUTTON2_MASK,
    193       event_flags & GDK_MOD1_MASK,
    194       event_flags & GDK_CONTROL_MASK,
    195       event_flags & GDK_META_MASK,
    196       event_flags & GDK_SHIFT_MASK);
    197 }
    198 
    199 }  // namespace event_utils
    200 
    201 namespace gtk_util {
    202 
    203 const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff);
    204 const GdkColor kGdkGray  = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
    205 const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00);
    206 const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00);
    207 
    208 GtkWidget* CreateLabeledControlsGroup(std::vector<GtkWidget*>* labels,
    209                                       const char* text, ...) {
    210   va_list ap;
    211   va_start(ap, text);
    212   GtkWidget* table = gtk_table_new(0, 2, FALSE);
    213   gtk_table_set_col_spacing(GTK_TABLE(table), 0, kLabelSpacing);
    214   gtk_table_set_row_spacings(GTK_TABLE(table), kControlSpacing);
    215 
    216   for (guint row = 0; text; ++row) {
    217     gtk_table_resize(GTK_TABLE(table), row + 1, 2);
    218     GtkWidget* control = va_arg(ap, GtkWidget*);
    219     GtkWidget* label = gtk_label_new(text);
    220     gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    221     if (labels)
    222       labels->push_back(label);
    223 
    224     gtk_table_attach(GTK_TABLE(table), label,
    225                  0, 1, row, row + 1,
    226                  GTK_FILL, GTK_FILL,
    227                  0, 0);
    228     gtk_table_attach_defaults(GTK_TABLE(table), control,
    229                               1, 2, row, row + 1);
    230     text = va_arg(ap, const char*);
    231   }
    232   va_end(ap);
    233 
    234   return table;
    235 }
    236 
    237 GtkWidget* CreateGtkBorderBin(GtkWidget* child, const GdkColor* color,
    238                               int top, int bottom, int left, int right) {
    239   // Use a GtkEventBox to get the background painted.  However, we can't just
    240   // use a container border, since it won't paint there.  Use an alignment
    241   // inside to get the sizes exactly of how we want the border painted.
    242   GtkWidget* ebox = gtk_event_box_new();
    243   if (color)
    244     gtk_widget_modify_bg(ebox, GTK_STATE_NORMAL, color);
    245   GtkWidget* alignment = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
    246   gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), top, bottom, left, right);
    247   gtk_container_add(GTK_CONTAINER(alignment), child);
    248   gtk_container_add(GTK_CONTAINER(ebox), alignment);
    249   return ebox;
    250 }
    251 
    252 GtkWidget* LeftAlignMisc(GtkWidget* misc) {
    253   gtk_misc_set_alignment(GTK_MISC(misc), 0, 0.5);
    254   return misc;
    255 }
    256 
    257 GtkWidget* CreateBoldLabel(const std::string& text) {
    258   GtkWidget* label = gtk_label_new(NULL);
    259   char* markup = g_markup_printf_escaped(kBoldLabelMarkup, text.c_str());
    260   gtk_label_set_markup(GTK_LABEL(label), markup);
    261   g_free(markup);
    262 
    263   return LeftAlignMisc(label);
    264 }
    265 
    266 void GetWidgetSizeFromCharacters(
    267     GtkWidget* widget, double width_chars, double height_lines,
    268     int* width, int* height) {
    269   DCHECK(GTK_WIDGET_REALIZED(widget))
    270       << " widget must be realized to compute font metrics correctly";
    271   PangoContext* context = gtk_widget_create_pango_context(widget);
    272   PangoFontMetrics* metrics = pango_context_get_metrics(context,
    273       widget->style->font_desc, pango_context_get_language(context));
    274   if (width) {
    275     *width = static_cast<int>(
    276         pango_font_metrics_get_approximate_char_width(metrics) *
    277         width_chars / PANGO_SCALE);
    278   }
    279   if (height) {
    280     *height = static_cast<int>(
    281         (pango_font_metrics_get_ascent(metrics) +
    282         pango_font_metrics_get_descent(metrics)) *
    283         height_lines / PANGO_SCALE);
    284   }
    285   pango_font_metrics_unref(metrics);
    286   g_object_unref(context);
    287 }
    288 
    289 void GetWidgetSizeFromResources(
    290     GtkWidget* widget, int width_chars, int height_lines,
    291     int* width, int* height) {
    292   DCHECK(GTK_WIDGET_REALIZED(widget))
    293       << " widget must be realized to compute font metrics correctly";
    294 
    295   double chars = 0;
    296   if (width)
    297     base::StringToDouble(l10n_util::GetStringUTF8(width_chars), &chars);
    298 
    299   double lines = 0;
    300   if (height)
    301     base::StringToDouble(l10n_util::GetStringUTF8(height_lines), &lines);
    302 
    303   GetWidgetSizeFromCharacters(widget, chars, lines, width, height);
    304 }
    305 
    306 void SetWindowSizeFromResources(GtkWindow* window,
    307                                 int width_id, int height_id, bool resizable) {
    308   int width = -1;
    309   int height = -1;
    310   gtk_util::GetWidgetSizeFromResources(GTK_WIDGET(window), width_id, height_id,
    311                                        (width_id != -1) ? &width : NULL,
    312                                        (height_id != -1) ? &height : NULL);
    313 
    314   if (resizable) {
    315     gtk_window_set_default_size(window, width, height);
    316   } else {
    317     // For a non-resizable window, GTK tries to snap the window size
    318     // to the minimum size around the content.  We use the sizes in
    319     // the resources to set *minimum* window size to allow windows
    320     // with long titles to be wide enough to display their titles.
    321     //
    322     // But if GTK wants to make the window *wider* due to very wide
    323     // controls, we should allow that too, so be careful to pick the
    324     // wider of the resources size and the natural window size.
    325 
    326     gtk_widget_show_all(GTK_BIN(window)->child);
    327     GtkRequisition requisition;
    328     gtk_widget_size_request(GTK_WIDGET(window), &requisition);
    329     gtk_widget_set_size_request(
    330         GTK_WIDGET(window),
    331         width == -1 ? -1 : std::max(width, requisition.width),
    332         height == -1 ? -1 : std::max(height, requisition.height));
    333   }
    334   gtk_window_set_resizable(window, resizable ? TRUE : FALSE);
    335 }
    336 
    337 void CenterOverWindow(GtkWindow* window, GtkWindow* parent) {
    338   gfx::Rect frame_bounds = gtk_util::GetWidgetScreenBounds(GTK_WIDGET(parent));
    339   gfx::Point origin = frame_bounds.origin();
    340   gfx::Size size = gtk_util::GetWidgetSize(GTK_WIDGET(window));
    341   origin.Offset(
    342       (frame_bounds.width() - size.width()) / 2,
    343       (frame_bounds.height() - size.height()) / 2);
    344 
    345   // Prevent moving window out of monitor bounds.
    346   GdkScreen* screen = gtk_window_get_screen(parent);
    347   if (screen) {
    348     // It would be better to check against workarea for given monitor
    349     // but getting workarea for particular monitor is tricky.
    350     gint monitor = gdk_screen_get_monitor_at_window(screen,
    351         GTK_WIDGET(parent)->window);
    352     GdkRectangle rect;
    353     gdk_screen_get_monitor_geometry(screen, monitor, &rect);
    354 
    355     // Check the right bottom corner.
    356     if (origin.x() > rect.x + rect.width - size.width())
    357       origin.set_x(rect.x + rect.width - size.width());
    358     if (origin.y() > rect.y + rect.height - size.height())
    359       origin.set_y(rect.y + rect.height - size.height());
    360 
    361     // Check the left top corner.
    362     if (origin.x() < rect.x)
    363       origin.set_x(rect.x);
    364     if (origin.y() < rect.y)
    365       origin.set_y(rect.y);
    366   }
    367 
    368   gtk_window_move(window, origin.x(), origin.y());
    369 
    370   // Move to user expected desktop if window is already visible.
    371   if (GTK_WIDGET(window)->window) {
    372     ui::ChangeWindowDesktop(
    373         ui::GetX11WindowFromGtkWidget(GTK_WIDGET(window)),
    374         ui::GetX11WindowFromGtkWidget(GTK_WIDGET(parent)));
    375   }
    376 }
    377 
    378 void MakeAppModalWindowGroup() {
    379 #if GTK_CHECK_VERSION(2, 14, 0)
    380   // Older versions of GTK+ don't give us gtk_window_group_list() which is what
    381   // we need to add current non-browser modal dialogs to the list. If
    382   // we have 2.14+ we can do things the correct way.
    383   GtkWindowGroup* window_group = gtk_window_group_new();
    384   for (BrowserList::const_iterator it = BrowserList::begin();
    385        it != BrowserList::end(); ++it) {
    386     // List all windows in this current group
    387     GtkWindowGroup* old_group =
    388         gtk_window_get_group((*it)->window()->GetNativeHandle());
    389 
    390     GList* all_windows = gtk_window_group_list_windows(old_group);
    391     for (GList* window = all_windows; window; window = window->next) {
    392       gtk_window_group_add_window(window_group, GTK_WINDOW(window->data));
    393     }
    394     g_list_free(all_windows);
    395   }
    396   g_object_unref(window_group);
    397 #else
    398   // Otherwise just grab all browser windows and be slightly broken.
    399   GtkWindowGroup* window_group = gtk_window_group_new();
    400   for (BrowserList::const_iterator it = BrowserList::begin();
    401        it != BrowserList::end(); ++it) {
    402     gtk_window_group_add_window(window_group,
    403                                 (*it)->window()->GetNativeHandle());
    404   }
    405   g_object_unref(window_group);
    406 #endif
    407 }
    408 
    409 void AppModalDismissedUngroupWindows() {
    410 #if GTK_CHECK_VERSION(2, 14, 0)
    411   if (BrowserList::begin() != BrowserList::end()) {
    412     std::vector<GtkWindow*> transient_windows;
    413 
    414     // All windows should be part of one big modal group right now.
    415     GtkWindowGroup* window_group = gtk_window_get_group(
    416         (*BrowserList::begin())->window()->GetNativeHandle());
    417     GList* windows = gtk_window_group_list_windows(window_group);
    418 
    419     for (GList* item = windows; item; item = item->next) {
    420       GtkWindow* window = GTK_WINDOW(item->data);
    421       GtkWindow* transient_for = gtk_window_get_transient_for(window);
    422       if (transient_for) {
    423         transient_windows.push_back(window);
    424       } else {
    425         GtkWindowGroup* window_group = gtk_window_group_new();
    426         gtk_window_group_add_window(window_group, window);
    427         g_object_unref(window_group);
    428       }
    429     }
    430 
    431     // Put each transient window in the same group as its transient parent.
    432     for (std::vector<GtkWindow*>::iterator it = transient_windows.begin();
    433          it != transient_windows.end(); ++it) {
    434       GtkWindow* transient_parent = gtk_window_get_transient_for(*it);
    435       GtkWindowGroup* group = gtk_window_get_group(transient_parent);
    436       gtk_window_group_add_window(group, *it);
    437     }
    438   }
    439 #else
    440   // This is slightly broken in the case where a different window had a dialog,
    441   // but its the best we can do since we don't have newer gtk stuff.
    442   for (BrowserList::const_iterator it = BrowserList::begin();
    443        it != BrowserList::end(); ++it) {
    444     GtkWindowGroup* window_group = gtk_window_group_new();
    445     gtk_window_group_add_window(window_group,
    446                                 (*it)->window()->GetNativeHandle());
    447     g_object_unref(window_group);
    448   }
    449 #endif
    450 }
    451 
    452 void RemoveAllChildren(GtkWidget* container) {
    453   gtk_container_foreach(GTK_CONTAINER(container), RemoveWidget, container);
    454 }
    455 
    456 void ForceFontSizePixels(GtkWidget* widget, double size_pixels) {
    457   GtkStyle* style = widget->style;
    458   PangoFontDescription* font_desc = style->font_desc;
    459   // pango_font_description_set_absolute_size sets the font size in device
    460   // units, which for us is pixels.
    461   pango_font_description_set_absolute_size(font_desc,
    462                                            PANGO_SCALE * size_pixels);
    463   gtk_widget_modify_font(widget, font_desc);
    464 }
    465 
    466 void UndoForceFontSize(GtkWidget* widget) {
    467   gtk_widget_modify_font(widget, NULL);
    468 }
    469 
    470 gfx::Point GetWidgetScreenPosition(GtkWidget* widget) {
    471   if (!widget->window) {
    472     NOTREACHED() << "Must only be called on realized widgets.";
    473     return gfx::Point(0, 0);
    474   }
    475 
    476   gint x, y;
    477   gdk_window_get_origin(widget->window, &x, &y);
    478 
    479   if (GTK_WIDGET_NO_WINDOW(widget)) {
    480     x += widget->allocation.x;
    481     y += widget->allocation.y;
    482   }
    483 
    484   return gfx::Point(x, y);
    485 }
    486 
    487 gfx::Rect GetWidgetScreenBounds(GtkWidget* widget) {
    488   gfx::Point position = GetWidgetScreenPosition(widget);
    489   return gfx::Rect(position.x(), position.y(),
    490                    widget->allocation.width, widget->allocation.height);
    491 }
    492 
    493 gfx::Size GetWidgetSize(GtkWidget* widget) {
    494   GtkRequisition size;
    495   gtk_widget_size_request(widget, &size);
    496   return gfx::Size(size.width, size.height);
    497 }
    498 
    499 void ConvertWidgetPointToScreen(GtkWidget* widget, gfx::Point* p) {
    500   DCHECK(widget);
    501   DCHECK(p);
    502 
    503   gfx::Point position = GetWidgetScreenPosition(widget);
    504   p->SetPoint(p->x() + position.x(), p->y() + position.y());
    505 }
    506 
    507 void InitRCStyles() {
    508   static const char kRCText[] =
    509       // Make our dialogs styled like the GNOME HIG.
    510       //
    511       // TODO(evanm): content-area-spacing was introduced in a later
    512       // version of GTK, so we need to set that manually on all dialogs.
    513       // Perhaps it would make sense to have a shared FixupDialog() function.
    514       "style \"gnome-dialog\" {\n"
    515       "  xthickness = 12\n"
    516       "  GtkDialog::action-area-border = 0\n"
    517       "  GtkDialog::button-spacing = 6\n"
    518       "  GtkDialog::content-area-spacing = 18\n"
    519       "  GtkDialog::content-area-border = 12\n"
    520       "}\n"
    521       // Note we set it at the "application" priority, so users can override.
    522       "widget \"GtkDialog\" style : application \"gnome-dialog\"\n"
    523 
    524       // Make our about dialog special, so the image is flush with the edge.
    525       "style \"about-dialog\" {\n"
    526       "  GtkDialog::action-area-border = 12\n"
    527       "  GtkDialog::button-spacing = 6\n"
    528       "  GtkDialog::content-area-spacing = 18\n"
    529       "  GtkDialog::content-area-border = 0\n"
    530       "}\n"
    531       "widget \"about-dialog\" style : application \"about-dialog\"\n";
    532 
    533   gtk_rc_parse_string(kRCText);
    534 }
    535 
    536 GtkWidget* CenterWidgetInHBox(GtkWidget* hbox, GtkWidget* widget,
    537                               bool pack_at_end, int padding) {
    538   GtkWidget* centering_vbox = gtk_vbox_new(FALSE, 0);
    539   gtk_box_pack_start(GTK_BOX(centering_vbox), widget, TRUE, FALSE, 0);
    540   if (pack_at_end)
    541     gtk_box_pack_end(GTK_BOX(hbox), centering_vbox, FALSE, FALSE, padding);
    542   else
    543     gtk_box_pack_start(GTK_BOX(hbox), centering_vbox, FALSE, FALSE, padding);
    544 
    545   return centering_vbox;
    546 }
    547 
    548 bool IsScreenComposited() {
    549   GdkScreen* screen = gdk_screen_get_default();
    550   return gdk_screen_is_composited(screen) == TRUE;
    551 }
    552 
    553 void EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate* delegate) {
    554   std::vector<XID> stack;
    555   if (!ui::GetXWindowStack(ui::GetX11RootWindow(), &stack)) {
    556     // Window Manager doesn't support _NET_CLIENT_LIST_STACKING, so fall back
    557     // to old school enumeration of all X windows.  Some WMs parent 'top-level'
    558     // windows in unnamed actual top-level windows (ion WM), so extend the
    559     // search depth to all children of top-level windows.
    560     const int kMaxSearchDepth = 1;
    561     ui::EnumerateAllWindows(delegate, kMaxSearchDepth);
    562     return;
    563   }
    564 
    565   std::vector<XID>::iterator iter;
    566   for (iter = stack.begin(); iter != stack.end(); iter++) {
    567     if (delegate->ShouldStopIterating(*iter))
    568       return;
    569   }
    570 }
    571 
    572 void SetButtonClickableByMouseButtons(GtkWidget* button,
    573                                       bool left, bool middle, bool right) {
    574   gint button_mask = 0;
    575   if (left)
    576     button_mask |= 1 << 1;
    577   if (middle)
    578     button_mask |= 1 << 2;
    579   if (right)
    580     button_mask |= 1 << 3;
    581   void* userdata = GINT_TO_POINTER(button_mask);
    582 
    583   g_signal_connect(button, "button-press-event",
    584                    G_CALLBACK(OnMouseButtonPressed), userdata);
    585   g_signal_connect(button, "button-release-event",
    586                    G_CALLBACK(OnMouseButtonReleased), userdata);
    587 }
    588 
    589 void SetButtonTriggersNavigation(GtkWidget* button) {
    590   SetButtonClickableByMouseButtons(button, true, true, false);
    591 }
    592 
    593 int MirroredLeftPointForRect(GtkWidget* widget, const gfx::Rect& bounds) {
    594   if (!base::i18n::IsRTL())
    595     return bounds.x();
    596   return widget->allocation.width - bounds.x() - bounds.width();
    597 }
    598 
    599 int MirroredXCoordinate(GtkWidget* widget, int x) {
    600   if (base::i18n::IsRTL())
    601     return widget->allocation.width - x;
    602   return x;
    603 }
    604 
    605 bool WidgetContainsCursor(GtkWidget* widget) {
    606   gint x = 0;
    607   gint y = 0;
    608   gtk_widget_get_pointer(widget, &x, &y);
    609   return WidgetBounds(widget).Contains(x, y);
    610 }
    611 
    612 void SetWindowIcon(GtkWindow* window) {
    613   GList* icon_list = GetIconList();
    614   gtk_window_set_icon_list(window, icon_list);
    615   g_list_free(icon_list);
    616 }
    617 
    618 void SetDefaultWindowIcon(GtkWindow* window) {
    619   GtkIconTheme* theme =
    620       gtk_icon_theme_get_for_screen(gtk_widget_get_screen(GTK_WIDGET(window)));
    621 
    622   if (gtk_icon_theme_has_icon(theme, kIconName)) {
    623     gtk_window_set_default_icon_name(kIconName);
    624     // Sometimes the WM fails to update the icon when we tell it to. The above
    625     // line should be enough to update all existing windows, but it can fail,
    626     // e.g. with Lucid/metacity. The following line seems to fix the common
    627     // case where the first window created doesn't have an icon.
    628     gtk_window_set_icon_name(window, kIconName);
    629   } else {
    630     GList* icon_list = GetIconList();
    631     gtk_window_set_default_icon_list(icon_list);
    632     // Same logic applies here.
    633     gtk_window_set_icon_list(window, icon_list);
    634     g_list_free(icon_list);
    635   }
    636 }
    637 
    638 GtkWidget* AddButtonToDialog(GtkWidget* dialog, const gchar* text,
    639                              const gchar* stock_id, gint response_id) {
    640   GtkWidget* button = gtk_button_new_with_label(text);
    641   gtk_button_set_image(GTK_BUTTON(button),
    642                        gtk_image_new_from_stock(stock_id,
    643                                                 GTK_ICON_SIZE_BUTTON));
    644   gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button,
    645                                response_id);
    646   return button;
    647 }
    648 
    649 GtkWidget* BuildDialogButton(GtkWidget* dialog, int ids_id,
    650                              const gchar* stock_id) {
    651   GtkWidget* button = gtk_button_new_with_mnemonic(
    652       gfx::ConvertAcceleratorsFromWindowsStyle(
    653           l10n_util::GetStringUTF8(ids_id)).c_str());
    654   gtk_button_set_image(GTK_BUTTON(button),
    655                        gtk_image_new_from_stock(stock_id,
    656                                                 GTK_ICON_SIZE_BUTTON));
    657   return button;
    658 }
    659 
    660 GtkWidget* CreateEntryImageHBox(GtkWidget* entry, GtkWidget* image) {
    661   GtkWidget* hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
    662   gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
    663   gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
    664   return hbox;
    665 }
    666 
    667 void SetLabelColor(GtkWidget* label, const GdkColor* color) {
    668   gtk_widget_modify_fg(label, GTK_STATE_NORMAL, color);
    669   gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, color);
    670   gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, color);
    671   gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, color);
    672 }
    673 
    674 GtkWidget* IndentWidget(GtkWidget* content) {
    675   GtkWidget* content_alignment = gtk_alignment_new(0.0, 0.5, 1.0, 1.0);
    676   gtk_alignment_set_padding(GTK_ALIGNMENT(content_alignment), 0, 0,
    677                             gtk_util::kGroupIndent, 0);
    678   gtk_container_add(GTK_CONTAINER(content_alignment), content);
    679   return content_alignment;
    680 }
    681 
    682 void UpdateGtkFontSettings(RendererPreferences* prefs) {
    683   DCHECK(prefs);
    684 
    685   // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
    686   // the default value for gtk-cursor-blink-time.
    687   static const gint kGtkDefaultCursorBlinkTime = 1200;
    688 
    689   gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
    690   gboolean cursor_blink = TRUE;
    691   gint antialias = 0;
    692   gint hinting = 0;
    693   gchar* hint_style = NULL;
    694   gchar* rgba_style = NULL;
    695   g_object_get(gtk_settings_get_default(),
    696                "gtk-cursor-blink-time", &cursor_blink_time,
    697                "gtk-cursor-blink", &cursor_blink,
    698                "gtk-xft-antialias", &antialias,
    699                "gtk-xft-hinting", &hinting,
    700                "gtk-xft-hintstyle", &hint_style,
    701                "gtk-xft-rgba", &rgba_style,
    702                NULL);
    703 
    704   // Set some reasonable defaults.
    705   prefs->should_antialias_text = true;
    706   prefs->hinting = RENDERER_PREFERENCES_HINTING_SYSTEM_DEFAULT;
    707   prefs->subpixel_rendering =
    708       RENDERER_PREFERENCES_SUBPIXEL_RENDERING_SYSTEM_DEFAULT;
    709 
    710   if (cursor_blink) {
    711     // Dividing by 2*1000ms follows the WebKit GTK port and makes the blink
    712     // frequency appear similar to the omnibox.  Without this the blink is too
    713     // slow.
    714     prefs->caret_blink_interval = cursor_blink_time / 2000.;
    715   } else {
    716     prefs->caret_blink_interval = 0;
    717   }
    718 
    719   // g_object_get() doesn't tell us whether the properties were present or not,
    720   // but if they aren't (because gnome-settings-daemon isn't running), we'll get
    721   // NULL values for the strings.
    722   if (hint_style && rgba_style) {
    723     prefs->should_antialias_text = antialias;
    724 
    725     if (hinting == 0 || strcmp(hint_style, "hintnone") == 0) {
    726       prefs->hinting = RENDERER_PREFERENCES_HINTING_NONE;
    727     } else if (strcmp(hint_style, "hintslight") == 0) {
    728       prefs->hinting = RENDERER_PREFERENCES_HINTING_SLIGHT;
    729     } else if (strcmp(hint_style, "hintmedium") == 0) {
    730       prefs->hinting = RENDERER_PREFERENCES_HINTING_MEDIUM;
    731     } else if (strcmp(hint_style, "hintfull") == 0) {
    732       prefs->hinting = RENDERER_PREFERENCES_HINTING_FULL;
    733     }
    734 
    735     if (strcmp(rgba_style, "none") == 0) {
    736       prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_NONE;
    737     } else if (strcmp(rgba_style, "rgb") == 0) {
    738       prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_RGB;
    739     } else if (strcmp(rgba_style, "bgr") == 0) {
    740       prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_BGR;
    741     } else if (strcmp(rgba_style, "vrgb") == 0) {
    742       prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VRGB;
    743     } else if (strcmp(rgba_style, "vbgr") == 0) {
    744       prefs->subpixel_rendering = RENDERER_PREFERENCES_SUBPIXEL_RENDERING_VBGR;
    745     }
    746   }
    747 
    748   if (hint_style)
    749     g_free(hint_style);
    750   if (rgba_style)
    751     g_free(rgba_style);
    752 }
    753 
    754 gfx::Point ScreenPoint(GtkWidget* widget) {
    755   int x, y;
    756   gdk_display_get_pointer(gtk_widget_get_display(widget), NULL, &x, &y,
    757                           NULL);
    758   return gfx::Point(x, y);
    759 }
    760 
    761 gfx::Point ClientPoint(GtkWidget* widget) {
    762   int x, y;
    763   gtk_widget_get_pointer(widget, &x, &y);
    764   return gfx::Point(x, y);
    765 }
    766 
    767 GdkPoint MakeBidiGdkPoint(gint x, gint y, gint width, bool ltr) {
    768   GdkPoint point = {ltr ? x : width - x, y};
    769   return point;
    770 }
    771 
    772 void DrawTextEntryBackground(GtkWidget* offscreen_entry,
    773                              GtkWidget* widget_to_draw_on,
    774                              GdkRectangle* dirty_rec,
    775                              GdkRectangle* rec) {
    776   GtkStyle* gtk_owned_style = gtk_rc_get_style(offscreen_entry);
    777   // GTK owns the above and we're going to have to make our own copy of it
    778   // that we can edit.
    779   GtkStyle* our_style = gtk_style_copy(gtk_owned_style);
    780   our_style = gtk_style_attach(our_style, widget_to_draw_on->window);
    781 
    782   // TODO(erg): Draw the focus ring if appropriate...
    783 
    784   // We're using GTK rendering; draw a GTK entry widget onto the background.
    785   gtk_paint_shadow(our_style, widget_to_draw_on->window,
    786                    GTK_STATE_NORMAL, GTK_SHADOW_IN, dirty_rec,
    787                    widget_to_draw_on, "entry",
    788                    rec->x, rec->y, rec->width, rec->height);
    789 
    790   // Draw the interior background (not all themes draw the entry background
    791   // above; this is a noop on themes that do).
    792   gint xborder = our_style->xthickness;
    793   gint yborder = our_style->ythickness;
    794   gint width = rec->width - 2 * xborder;
    795   gint height = rec->height - 2 * yborder;
    796   if (width > 0 && height > 0) {
    797     gtk_paint_flat_box(our_style, widget_to_draw_on->window,
    798                        GTK_STATE_NORMAL, GTK_SHADOW_NONE, dirty_rec,
    799                        widget_to_draw_on, "entry_bg",
    800                        rec->x + xborder, rec->y + yborder,
    801                        width, height);
    802   }
    803 
    804   gtk_style_detach(our_style);
    805   g_object_unref(our_style);
    806 }
    807 
    808 void DrawThemedToolbarBackground(GtkWidget* widget,
    809                                  cairo_t* cr,
    810                                  GdkEventExpose* event,
    811                                  const gfx::Point& tabstrip_origin,
    812                                  GtkThemeService* theme_service) {
    813   // Fill the entire region with the toolbar color.
    814   GdkColor color = theme_service->GetGdkColor(
    815       ThemeService::COLOR_TOOLBAR);
    816   gdk_cairo_set_source_color(cr, &color);
    817   cairo_fill(cr);
    818 
    819   // The toolbar is supposed to blend in with the active tab, so we have to pass
    820   // coordinates for the IDR_THEME_TOOLBAR bitmap relative to the top of the
    821   // tab strip.
    822   CairoCachedSurface* background = theme_service->GetSurfaceNamed(
    823       IDR_THEME_TOOLBAR, widget);
    824   background->SetSource(cr, tabstrip_origin.x(), tabstrip_origin.y());
    825   // We tile the toolbar background in both directions.
    826   cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
    827   cairo_rectangle(cr,
    828                   tabstrip_origin.x(),
    829                   tabstrip_origin.y(),
    830                   event->area.x + event->area.width - tabstrip_origin.x(),
    831                   event->area.y + event->area.height - tabstrip_origin.y());
    832   cairo_fill(cr);
    833 }
    834 
    835 GdkColor AverageColors(GdkColor color_one, GdkColor color_two) {
    836   GdkColor average_color;
    837   average_color.pixel = 0;
    838   average_color.red = (color_one.red + color_two.red) / 2;
    839   average_color.green = (color_one.green + color_two.green) / 2;
    840   average_color.blue = (color_one.blue + color_two.blue) / 2;
    841   return average_color;
    842 }
    843 
    844 void SetAlwaysShowImage(GtkWidget* image_menu_item) {
    845   // Compile time check: if it's available, just use the API.
    846   // GTK_CHECK_VERSION is TRUE if the passed version is compatible.
    847 #if GTK_CHECK_VERSION(2, 16, 1)
    848   gtk_image_menu_item_set_always_show_image(
    849       GTK_IMAGE_MENU_ITEM(image_menu_item), TRUE);
    850 #else
    851   // Run time check: if the API is not available, set the property manually.
    852   // This will still only work with GTK 2.16+ as the property doesn't exist
    853   // in earlier versions.
    854   // gtk_check_version() returns NULL if the passed version is compatible.
    855   if (!gtk_check_version(2, 16, 1)) {
    856     GValue true_value = { 0 };
    857     g_value_init(&true_value, G_TYPE_BOOLEAN);
    858     g_value_set_boolean(&true_value, TRUE);
    859     g_object_set_property(G_OBJECT(image_menu_item), "always-show-image",
    860                           &true_value);
    861   }
    862 #endif
    863 }
    864 
    865 gfx::Rect GetWidgetRectRelativeToToplevel(GtkWidget* widget) {
    866   DCHECK(GTK_WIDGET_REALIZED(widget));
    867 
    868   GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
    869   DCHECK(toplevel);
    870   DCHECK(GTK_WIDGET_REALIZED(toplevel));
    871 
    872   gint x = 0, y = 0;
    873   gtk_widget_translate_coordinates(widget,
    874                                    toplevel,
    875                                    0, 0,
    876                                    &x, &y);
    877   return gfx::Rect(x, y, widget->allocation.width, widget->allocation.height);
    878 }
    879 
    880 void SuppressDefaultPainting(GtkWidget* container) {
    881   g_signal_connect(container, "expose-event",
    882                    G_CALLBACK(PaintNoBackground), NULL);
    883 }
    884 
    885 WindowOpenDisposition DispositionForCurrentButtonPressEvent() {
    886   GdkEvent* event = gtk_get_current_event();
    887   if (!event) {
    888     NOTREACHED();
    889     return NEW_FOREGROUND_TAB;
    890   }
    891 
    892   guint state = event->button.state;
    893   gdk_event_free(event);
    894   return event_utils::DispositionFromEventFlags(state);
    895 }
    896 
    897 bool GrabAllInput(GtkWidget* widget) {
    898   guint time = gtk_get_current_event_time();
    899 
    900   if (!GTK_WIDGET_VISIBLE(widget))
    901     return false;
    902 
    903   if (!gdk_pointer_grab(widget->window, TRUE,
    904                         GdkEventMask(GDK_BUTTON_PRESS_MASK |
    905                                      GDK_BUTTON_RELEASE_MASK |
    906                                      GDK_ENTER_NOTIFY_MASK |
    907                                      GDK_LEAVE_NOTIFY_MASK |
    908                                      GDK_POINTER_MOTION_MASK),
    909                         NULL, NULL, time) == 0) {
    910     return false;
    911   }
    912 
    913   if (!gdk_keyboard_grab(widget->window, TRUE, time) == 0) {
    914     gdk_display_pointer_ungrab(gdk_drawable_get_display(widget->window), time);
    915     return false;
    916   }
    917 
    918   gtk_grab_add(widget);
    919   return true;
    920 }
    921 
    922 gfx::Rect WidgetBounds(GtkWidget* widget) {
    923   // To quote the gtk docs:
    924   //
    925   //   Widget coordinates are a bit odd; for historical reasons, they are
    926   //   defined as widget->window coordinates for widgets that are not
    927   //   GTK_NO_WINDOW widgets, and are relative to widget->allocation.x,
    928   //   widget->allocation.y for widgets that are GTK_NO_WINDOW widgets.
    929   //
    930   // So the base is always (0,0).
    931   return gfx::Rect(0, 0, widget->allocation.width, widget->allocation.height);
    932 }
    933 
    934 void SetWMLastUserActionTime(GtkWindow* window) {
    935   gdk_x11_window_set_user_time(GTK_WIDGET(window)->window, XTimeNow());
    936 }
    937 
    938 guint32 XTimeNow() {
    939   struct timespec ts;
    940   clock_gettime(CLOCK_MONOTONIC, &ts);
    941   return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
    942 }
    943 
    944 bool URLFromPrimarySelection(Profile* profile, GURL* url) {
    945   GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
    946   DCHECK(clipboard);
    947   gchar* selection_text = gtk_clipboard_wait_for_text(clipboard);
    948   if (!selection_text)
    949     return false;
    950 
    951   // Use autocomplete to clean up the text, going so far as to turn it into
    952   // a search query if necessary.
    953   AutocompleteMatch match;
    954   profile->GetAutocompleteClassifier()->Classify(UTF8ToUTF16(selection_text),
    955       string16(), false, &match, NULL);
    956   g_free(selection_text);
    957   if (!match.destination_url.is_valid())
    958     return false;
    959 
    960   *url = match.destination_url;
    961   return true;
    962 }
    963 
    964 bool AddWindowAlphaChannel(GtkWidget* window) {
    965   GdkScreen* screen = gtk_widget_get_screen(window);
    966   GdkColormap* rgba = gdk_screen_get_rgba_colormap(screen);
    967   if (rgba)
    968     gtk_widget_set_colormap(window, rgba);
    969 
    970   return rgba;
    971 }
    972 
    973 void GetTextColors(GdkColor* normal_base,
    974                    GdkColor* selected_base,
    975                    GdkColor* normal_text,
    976                    GdkColor* selected_text) {
    977   GtkWidget* fake_entry = gtk_entry_new();
    978   GtkStyle* style = gtk_rc_get_style(fake_entry);
    979 
    980   if (normal_base)
    981     *normal_base = style->base[GTK_STATE_NORMAL];
    982   if (selected_base)
    983     *selected_base = style->base[GTK_STATE_SELECTED];
    984   if (normal_text)
    985     *normal_text = style->text[GTK_STATE_NORMAL];
    986   if (selected_text)
    987     *selected_text = style->text[GTK_STATE_SELECTED];
    988 
    989   g_object_ref_sink(fake_entry);
    990   g_object_unref(fake_entry);
    991 }
    992 
    993 #if defined(OS_CHROMEOS)
    994 
    995 GtkWindow* GetLastActiveBrowserWindow() {
    996   if (Browser* b = BrowserList::GetLastActive()) {
    997     if (b->type() != Browser::TYPE_NORMAL) {
    998       b = BrowserList::FindBrowserWithType(b->profile(),
    999                                            Browser::TYPE_NORMAL,
   1000                                            true);
   1001     }
   1002 
   1003     if (b)
   1004       return GTK_WINDOW(b->window()->GetNativeHandle());
   1005   }
   1006 
   1007   return NULL;
   1008 }
   1009 
   1010 int GetNativeDialogFlags(GtkWindow* dialog) {
   1011   int flags = chromeos::DIALOG_FLAG_DEFAULT;
   1012 
   1013   if (gtk_window_get_resizable(dialog))
   1014     flags |= chromeos::DIALOG_FLAG_RESIZEABLE;
   1015   if (gtk_window_get_modal(dialog))
   1016     flags |= chromeos::DIALOG_FLAG_MODAL;
   1017 
   1018   return flags;
   1019 }
   1020 
   1021 GtkWindow* GetDialogTransientParent(GtkWindow* dialog) {
   1022   GtkWindow* parent = gtk_window_get_transient_for(dialog);
   1023   if (!parent)
   1024     parent = GetLastActiveBrowserWindow();
   1025 
   1026   return parent;
   1027 }
   1028 
   1029 void ShowDialog(GtkWidget* dialog) {
   1030   // Make sure all controls are visible so that we get correct size.
   1031   gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
   1032 
   1033   // Get dialog window size.
   1034   gint width = 0;
   1035   gint height = 0;
   1036   gtk_window_get_size(GTK_WINDOW(dialog), &width, &height);
   1037 
   1038   chromeos::ShowNativeDialog(GetDialogTransientParent(GTK_WINDOW(dialog)),
   1039       dialog,
   1040       GetNativeDialogFlags(GTK_WINDOW(dialog)),
   1041       gfx::Size(width, height),
   1042       gfx::Size());
   1043 }
   1044 
   1045 void ShowDialogWithLocalizedSize(GtkWidget* dialog,
   1046                                  int width_id,
   1047                                  int height_id,
   1048                                  bool resizeable) {
   1049   int width = (width_id == -1) ? 0 :
   1050       views::Window::GetLocalizedContentsWidth(width_id);
   1051   int height = (height_id == -1) ? 0 :
   1052       views::Window::GetLocalizedContentsHeight(height_id);
   1053 
   1054   chromeos::ShowNativeDialog(GetDialogTransientParent(GTK_WINDOW(dialog)),
   1055       dialog,
   1056       resizeable ? chromeos::DIALOG_FLAG_RESIZEABLE :
   1057                    chromeos::DIALOG_FLAG_DEFAULT,
   1058       gfx::Size(width, height),
   1059       gfx::Size());
   1060 }
   1061 
   1062 void ShowDialogWithMinLocalizedWidth(GtkWidget* dialog,
   1063                                      int width_id) {
   1064   int width = (width_id == -1) ? 0 :
   1065       views::Window::GetLocalizedContentsWidth(width_id);
   1066 
   1067   chromeos::ShowNativeDialog(GetDialogTransientParent(GTK_WINDOW(dialog)),
   1068       dialog,
   1069       GetNativeDialogFlags(GTK_WINDOW(dialog)),
   1070       gfx::Size(),
   1071       gfx::Size(width, 0));
   1072 }
   1073 
   1074 void PresentWindow(GtkWidget* window, int timestamp) {
   1075   GtkWindow* host_window = chromeos::GetNativeDialogWindow(window);
   1076   if (!host_window)
   1077       host_window = GTK_WINDOW(window);
   1078   if (timestamp)
   1079     gtk_window_present_with_time(host_window, timestamp);
   1080   else
   1081     gtk_window_present(host_window);
   1082 }
   1083 
   1084 GtkWindow* GetDialogWindow(GtkWidget* dialog) {
   1085   return chromeos::GetNativeDialogWindow(dialog);
   1086 }
   1087 
   1088 gfx::Rect GetDialogBounds(GtkWidget* dialog) {
   1089   return chromeos::GetNativeDialogContentsBounds(dialog);
   1090 }
   1091 
   1092 #else
   1093 
   1094 void ShowDialog(GtkWidget* dialog) {
   1095   gtk_widget_show_all(dialog);
   1096 }
   1097 
   1098 void ShowDialogWithLocalizedSize(GtkWidget* dialog,
   1099                                  int width_id,
   1100                                  int height_id,
   1101                                  bool resizeable) {
   1102   gtk_widget_realize(dialog);
   1103   SetWindowSizeFromResources(GTK_WINDOW(dialog),
   1104                              width_id,
   1105                              height_id,
   1106                              resizeable);
   1107   gtk_widget_show_all(dialog);
   1108 }
   1109 
   1110 void ShowDialogWithMinLocalizedWidth(GtkWidget* dialog,
   1111                                      int width_id) {
   1112   gtk_widget_show_all(dialog);
   1113 
   1114   // Suggest a minimum size.
   1115   gint width;
   1116   GtkRequisition req;
   1117   gtk_widget_size_request(dialog, &req);
   1118   gtk_util::GetWidgetSizeFromResources(dialog, width_id, 0, &width, NULL);
   1119   if (width > req.width)
   1120     gtk_widget_set_size_request(dialog, width, -1);
   1121 }
   1122 
   1123 void PresentWindow(GtkWidget* window, int timestamp) {
   1124   if (timestamp)
   1125     gtk_window_present_with_time(GTK_WINDOW(window), timestamp);
   1126   else
   1127     gtk_window_present(GTK_WINDOW(window));
   1128 }
   1129 
   1130 GtkWindow* GetDialogWindow(GtkWidget* dialog) {
   1131   return GTK_WINDOW(dialog);
   1132 }
   1133 
   1134 gfx::Rect GetDialogBounds(GtkWidget* dialog) {
   1135   gint x = 0, y = 0, width = 1, height = 1;
   1136   gtk_window_get_position(GTK_WINDOW(dialog), &x, &y);
   1137   gtk_window_get_size(GTK_WINDOW(dialog), &width, &height);
   1138 
   1139   return gfx::Rect(x, y, width, height);
   1140 }
   1141 
   1142 #endif
   1143 
   1144 string16 GetStockPreferencesMenuLabel() {
   1145   GtkStockItem stock_item;
   1146   string16 preferences;
   1147   if (gtk_stock_lookup(GTK_STOCK_PREFERENCES, &stock_item)) {
   1148     const char16 kUnderscore[] = { '_', 0 };
   1149     RemoveChars(UTF8ToUTF16(stock_item.label), kUnderscore, &preferences);
   1150   }
   1151   return preferences;
   1152 }
   1153 
   1154 bool IsWidgetAncestryVisible(GtkWidget* widget) {
   1155   GtkWidget* parent = widget;
   1156   while (parent && GTK_WIDGET_VISIBLE(parent))
   1157     parent = parent->parent;
   1158   return !parent;
   1159 }
   1160 
   1161 void SetLabelWidth(GtkWidget* label, int pixel_width) {
   1162   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
   1163   gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
   1164 
   1165   // Do the simple thing in LTR because the bug only affects right-aligned
   1166   // text. Also, when using the workaround, the label tries to maintain
   1167   // uniform line-length, which we don't really want.
   1168   if (gtk_widget_get_direction(label) == GTK_TEXT_DIR_LTR) {
   1169     gtk_widget_set_size_request(label, pixel_width, -1);
   1170   } else {
   1171     // The label has to be realized before we can adjust its width.
   1172     if (GTK_WIDGET_REALIZED(label)) {
   1173       OnLabelRealize(label, GINT_TO_POINTER(pixel_width));
   1174     } else {
   1175       g_signal_connect(label, "realize", G_CALLBACK(OnLabelRealize),
   1176                        GINT_TO_POINTER(pixel_width));
   1177     }
   1178   }
   1179 }
   1180 
   1181 void InitLabelSizeRequestAndEllipsizeMode(GtkWidget* label) {
   1182   GtkRequisition size;
   1183   gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_NONE);
   1184   gtk_widget_set_size_request(label, -1, -1);
   1185   gtk_widget_size_request(label, &size);
   1186   gtk_widget_set_size_request(label, size.width, size.height);
   1187   gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
   1188 }
   1189 
   1190 GdkDragAction WebDragOpToGdkDragAction(WebDragOperationsMask op) {
   1191   GdkDragAction action = static_cast<GdkDragAction>(0);
   1192   if (op & WebDragOperationCopy)
   1193     action = static_cast<GdkDragAction>(action | GDK_ACTION_COPY);
   1194   if (op & WebDragOperationLink)
   1195     action = static_cast<GdkDragAction>(action | GDK_ACTION_LINK);
   1196   if (op & WebDragOperationMove)
   1197     action = static_cast<GdkDragAction>(action | GDK_ACTION_MOVE);
   1198   return action;
   1199 }
   1200 
   1201 WebDragOperationsMask GdkDragActionToWebDragOp(GdkDragAction action) {
   1202   WebDragOperationsMask op = WebDragOperationNone;
   1203   if (action & GDK_ACTION_COPY)
   1204     op = static_cast<WebDragOperationsMask>(op | WebDragOperationCopy);
   1205   if (action & GDK_ACTION_LINK)
   1206     op = static_cast<WebDragOperationsMask>(op | WebDragOperationLink);
   1207   if (action & GDK_ACTION_MOVE)
   1208     op = static_cast<WebDragOperationsMask>(op | WebDragOperationMove);
   1209   return op;
   1210 }
   1211 
   1212 void ApplyMessageDialogQuirks(GtkWidget* dialog) {
   1213   if (gtk_window_get_modal(GTK_WINDOW(dialog))) {
   1214     // Work around a KDE 3 window manager bug.
   1215     scoped_ptr<base::Environment> env(base::Environment::Create());
   1216     if (base::nix::DESKTOP_ENVIRONMENT_KDE3 ==
   1217         base::nix::GetDesktopEnvironment(env.get()))
   1218       gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), FALSE);
   1219   }
   1220 }
   1221 
   1222 // Performs Cut/Copy/Paste operation on the |window|.
   1223 // If the current render view is focused, then just call the specified |method|
   1224 // against the current render view host, otherwise emit the specified |signal|
   1225 // against the focused widget.
   1226 // TODO(suzhe): This approach does not work for plugins.
   1227 void DoCutCopyPaste(BrowserWindow* window,
   1228                     void (RenderViewHost::*method)(),
   1229                     const char* signal) {
   1230   GtkWidget* widget = GetBrowserWindowFocusedWidget(window);
   1231   if (widget == NULL)
   1232     return;  // Do nothing if no focused widget.
   1233 
   1234   TabContents* current_tab = GetBrowserWindowSelectedTabContents(window);
   1235   if (current_tab && widget == current_tab->GetContentNativeView()) {
   1236     (current_tab->render_view_host()->*method)();
   1237   } else {
   1238     guint id;
   1239     if ((id = g_signal_lookup(signal, G_OBJECT_TYPE(widget))) != 0)
   1240       g_signal_emit(widget, id, 0);
   1241   }
   1242 }
   1243 
   1244 void DoCut(BrowserWindow* window) {
   1245   DoCutCopyPaste(window, &RenderViewHost::Cut, "cut-clipboard");
   1246 }
   1247 
   1248 void DoCopy(BrowserWindow* window) {
   1249   DoCutCopyPaste(window, &RenderViewHost::Copy, "copy-clipboard");
   1250 }
   1251 
   1252 void DoPaste(BrowserWindow* window) {
   1253   DoCutCopyPaste(window, &RenderViewHost::Paste, "paste-clipboard");
   1254 }
   1255 
   1256 }  // namespace gtk_util
   1257