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
50using WebKit::WebDragOperationsMask;
51using WebKit::WebDragOperation;
52using WebKit::WebDragOperationNone;
53using WebKit::WebDragOperationCopy;
54using WebKit::WebDragOperationLink;
55using WebKit::WebDragOperationMove;
56
57namespace {
58
59#if defined(GOOGLE_CHROME_BUILD)
60static const char* kIconName = "google-chrome";
61#else
62static const char* kIconName = "chromium-browser";
63#endif
64
65const char kBoldLabelMarkup[] = "<span weight='bold'>%s</span>";
66
67// Callback used in RemoveAllChildren.
68void 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.
74gboolean 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
90gboolean 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.
101int 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
121void 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.
128GList* 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.
138gboolean 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
154TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
155  chromeos::BrowserView* browser_view = static_cast<chromeos::BrowserView*>(
156      window);
157  return browser_view->GetSelectedTabContents();
158}
159
160GtkWidget* 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
174TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
175  BrowserWindowGtk* browser_window = static_cast<BrowserWindowGtk*>(
176      window);
177  return browser_window->browser()->GetSelectedTabContents();
178}
179
180GtkWidget* GetBrowserWindowFocusedWidget(BrowserWindow* window) {
181  return gtk_window_get_focus(window->GetNativeHandle());
182}
183
184#endif
185
186}  // namespace
187
188namespace event_utils {
189
190WindowOpenDisposition 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
201namespace gtk_util {
202
203const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff);
204const GdkColor kGdkGray  = GDK_COLOR_RGB(0x7f, 0x7f, 0x7f);
205const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00);
206const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00);
207
208GtkWidget* 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
237GtkWidget* 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
252GtkWidget* LeftAlignMisc(GtkWidget* misc) {
253  gtk_misc_set_alignment(GTK_MISC(misc), 0, 0.5);
254  return misc;
255}
256
257GtkWidget* 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
266void 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
289void 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
306void 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
337void 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
378void 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
409void 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
452void RemoveAllChildren(GtkWidget* container) {
453  gtk_container_foreach(GTK_CONTAINER(container), RemoveWidget, container);
454}
455
456void 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
466void UndoForceFontSize(GtkWidget* widget) {
467  gtk_widget_modify_font(widget, NULL);
468}
469
470gfx::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
487gfx::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
493gfx::Size GetWidgetSize(GtkWidget* widget) {
494  GtkRequisition size;
495  gtk_widget_size_request(widget, &size);
496  return gfx::Size(size.width, size.height);
497}
498
499void 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
507void 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
536GtkWidget* 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
548bool IsScreenComposited() {
549  GdkScreen* screen = gdk_screen_get_default();
550  return gdk_screen_is_composited(screen) == TRUE;
551}
552
553void 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
572void 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
589void SetButtonTriggersNavigation(GtkWidget* button) {
590  SetButtonClickableByMouseButtons(button, true, true, false);
591}
592
593int 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
599int MirroredXCoordinate(GtkWidget* widget, int x) {
600  if (base::i18n::IsRTL())
601    return widget->allocation.width - x;
602  return x;
603}
604
605bool 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
612void 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
618void 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
638GtkWidget* 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
649GtkWidget* 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
660GtkWidget* 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
667void 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
674GtkWidget* 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
682void 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
754gfx::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
761gfx::Point ClientPoint(GtkWidget* widget) {
762  int x, y;
763  gtk_widget_get_pointer(widget, &x, &y);
764  return gfx::Point(x, y);
765}
766
767GdkPoint MakeBidiGdkPoint(gint x, gint y, gint width, bool ltr) {
768  GdkPoint point = {ltr ? x : width - x, y};
769  return point;
770}
771
772void 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
808void 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
835GdkColor 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
844void 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
865gfx::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
880void SuppressDefaultPainting(GtkWidget* container) {
881  g_signal_connect(container, "expose-event",
882                   G_CALLBACK(PaintNoBackground), NULL);
883}
884
885WindowOpenDisposition 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
897bool 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
922gfx::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
934void SetWMLastUserActionTime(GtkWindow* window) {
935  gdk_x11_window_set_user_time(GTK_WIDGET(window)->window, XTimeNow());
936}
937
938guint32 XTimeNow() {
939  struct timespec ts;
940  clock_gettime(CLOCK_MONOTONIC, &ts);
941  return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
942}
943
944bool 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
964bool 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
973void 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
995GtkWindow* 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
1010int 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
1021GtkWindow* GetDialogTransientParent(GtkWindow* dialog) {
1022  GtkWindow* parent = gtk_window_get_transient_for(dialog);
1023  if (!parent)
1024    parent = GetLastActiveBrowserWindow();
1025
1026  return parent;
1027}
1028
1029void 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
1045void 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
1062void 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
1074void 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
1084GtkWindow* GetDialogWindow(GtkWidget* dialog) {
1085  return chromeos::GetNativeDialogWindow(dialog);
1086}
1087
1088gfx::Rect GetDialogBounds(GtkWidget* dialog) {
1089  return chromeos::GetNativeDialogContentsBounds(dialog);
1090}
1091
1092#else
1093
1094void ShowDialog(GtkWidget* dialog) {
1095  gtk_widget_show_all(dialog);
1096}
1097
1098void 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
1110void 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
1123void 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
1130GtkWindow* GetDialogWindow(GtkWidget* dialog) {
1131  return GTK_WINDOW(dialog);
1132}
1133
1134gfx::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
1144string16 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
1154bool IsWidgetAncestryVisible(GtkWidget* widget) {
1155  GtkWidget* parent = widget;
1156  while (parent && GTK_WIDGET_VISIBLE(parent))
1157    parent = parent->parent;
1158  return !parent;
1159}
1160
1161void 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
1181void 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
1190GdkDragAction 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
1201WebDragOperationsMask 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
1212void 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.
1227void 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
1244void DoCut(BrowserWindow* window) {
1245  DoCutCopyPaste(window, &RenderViewHost::Cut, "cut-clipboard");
1246}
1247
1248void DoCopy(BrowserWindow* window) {
1249  DoCutCopyPaste(window, &RenderViewHost::Copy, "copy-clipboard");
1250}
1251
1252void DoPaste(BrowserWindow* window) {
1253  DoCutCopyPaste(window, &RenderViewHost::Paste, "paste-clipboard");
1254}
1255
1256}  // namespace gtk_util
1257