• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
RemoveWidget(GtkWidget * widget,gpointer container)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.
OnMouseButtonPressed(GtkWidget * widget,GdkEventButton * event,gpointer userdata)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 
OnMouseButtonReleased(GtkWidget * widget,GdkEventButton * event,gpointer userdata)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.
GetCharacterWidthForPixels(GtkWidget * widget,int pixel_width)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 
OnLabelRealize(GtkWidget * label,gpointer pixel_width)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.
GetIconList()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.
PaintNoBackground(GtkWidget * widget,GdkEventExpose * event,gpointer unused)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 
GetBrowserWindowSelectedTabContents(BrowserWindow * window)154 TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
155   chromeos::BrowserView* browser_view = static_cast<chromeos::BrowserView*>(
156       window);
157   return browser_view->GetSelectedTabContents();
158 }
159 
GetBrowserWindowFocusedWidget(BrowserWindow * window)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 
GetBrowserWindowSelectedTabContents(BrowserWindow * window)174 TabContents* GetBrowserWindowSelectedTabContents(BrowserWindow* window) {
175   BrowserWindowGtk* browser_window = static_cast<BrowserWindowGtk*>(
176       window);
177   return browser_window->browser()->GetSelectedTabContents();
178 }
179 
GetBrowserWindowFocusedWidget(BrowserWindow * window)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 
DispositionFromEventFlags(guint event_flags)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 
CreateLabeledControlsGroup(std::vector<GtkWidget * > * labels,const char * text,...)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 
CreateGtkBorderBin(GtkWidget * child,const GdkColor * color,int top,int bottom,int left,int right)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 
LeftAlignMisc(GtkWidget * misc)252 GtkWidget* LeftAlignMisc(GtkWidget* misc) {
253   gtk_misc_set_alignment(GTK_MISC(misc), 0, 0.5);
254   return misc;
255 }
256 
CreateBoldLabel(const std::string & text)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 
GetWidgetSizeFromCharacters(GtkWidget * widget,double width_chars,double height_lines,int * width,int * height)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 
GetWidgetSizeFromResources(GtkWidget * widget,int width_chars,int height_lines,int * width,int * height)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 
SetWindowSizeFromResources(GtkWindow * window,int width_id,int height_id,bool resizable)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 
CenterOverWindow(GtkWindow * window,GtkWindow * parent)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 
MakeAppModalWindowGroup()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 
AppModalDismissedUngroupWindows()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 
RemoveAllChildren(GtkWidget * container)452 void RemoveAllChildren(GtkWidget* container) {
453   gtk_container_foreach(GTK_CONTAINER(container), RemoveWidget, container);
454 }
455 
ForceFontSizePixels(GtkWidget * widget,double size_pixels)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 
UndoForceFontSize(GtkWidget * widget)466 void UndoForceFontSize(GtkWidget* widget) {
467   gtk_widget_modify_font(widget, NULL);
468 }
469 
GetWidgetScreenPosition(GtkWidget * widget)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 
GetWidgetScreenBounds(GtkWidget * widget)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 
GetWidgetSize(GtkWidget * widget)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 
ConvertWidgetPointToScreen(GtkWidget * widget,gfx::Point * p)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 
InitRCStyles()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 
CenterWidgetInHBox(GtkWidget * hbox,GtkWidget * widget,bool pack_at_end,int padding)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 
IsScreenComposited()548 bool IsScreenComposited() {
549   GdkScreen* screen = gdk_screen_get_default();
550   return gdk_screen_is_composited(screen) == TRUE;
551 }
552 
EnumerateTopLevelWindows(ui::EnumerateWindowsDelegate * delegate)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 
SetButtonClickableByMouseButtons(GtkWidget * button,bool left,bool middle,bool right)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 
SetButtonTriggersNavigation(GtkWidget * button)589 void SetButtonTriggersNavigation(GtkWidget* button) {
590   SetButtonClickableByMouseButtons(button, true, true, false);
591 }
592 
MirroredLeftPointForRect(GtkWidget * widget,const gfx::Rect & bounds)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 
MirroredXCoordinate(GtkWidget * widget,int x)599 int MirroredXCoordinate(GtkWidget* widget, int x) {
600   if (base::i18n::IsRTL())
601     return widget->allocation.width - x;
602   return x;
603 }
604 
WidgetContainsCursor(GtkWidget * widget)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 
SetWindowIcon(GtkWindow * window)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 
SetDefaultWindowIcon(GtkWindow * window)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 
AddButtonToDialog(GtkWidget * dialog,const gchar * text,const gchar * stock_id,gint response_id)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 
BuildDialogButton(GtkWidget * dialog,int ids_id,const gchar * stock_id)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 
CreateEntryImageHBox(GtkWidget * entry,GtkWidget * image)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 
SetLabelColor(GtkWidget * label,const GdkColor * color)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 
IndentWidget(GtkWidget * content)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 
UpdateGtkFontSettings(RendererPreferences * prefs)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 
ScreenPoint(GtkWidget * widget)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 
ClientPoint(GtkWidget * widget)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 
MakeBidiGdkPoint(gint x,gint y,gint width,bool ltr)767 GdkPoint MakeBidiGdkPoint(gint x, gint y, gint width, bool ltr) {
768   GdkPoint point = {ltr ? x : width - x, y};
769   return point;
770 }
771 
DrawTextEntryBackground(GtkWidget * offscreen_entry,GtkWidget * widget_to_draw_on,GdkRectangle * dirty_rec,GdkRectangle * rec)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 
DrawThemedToolbarBackground(GtkWidget * widget,cairo_t * cr,GdkEventExpose * event,const gfx::Point & tabstrip_origin,GtkThemeService * theme_service)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 
AverageColors(GdkColor color_one,GdkColor color_two)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 
SetAlwaysShowImage(GtkWidget * image_menu_item)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 
GetWidgetRectRelativeToToplevel(GtkWidget * widget)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 
SuppressDefaultPainting(GtkWidget * container)880 void SuppressDefaultPainting(GtkWidget* container) {
881   g_signal_connect(container, "expose-event",
882                    G_CALLBACK(PaintNoBackground), NULL);
883 }
884 
DispositionForCurrentButtonPressEvent()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 
GrabAllInput(GtkWidget * widget)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 
WidgetBounds(GtkWidget * widget)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 
SetWMLastUserActionTime(GtkWindow * window)934 void SetWMLastUserActionTime(GtkWindow* window) {
935   gdk_x11_window_set_user_time(GTK_WIDGET(window)->window, XTimeNow());
936 }
937 
XTimeNow()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 
URLFromPrimarySelection(Profile * profile,GURL * url)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 
AddWindowAlphaChannel(GtkWidget * window)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 
GetTextColors(GdkColor * normal_base,GdkColor * selected_base,GdkColor * normal_text,GdkColor * selected_text)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 
GetLastActiveBrowserWindow()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 
GetNativeDialogFlags(GtkWindow * dialog)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 
GetDialogTransientParent(GtkWindow * dialog)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 
ShowDialog(GtkWidget * dialog)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 
ShowDialogWithLocalizedSize(GtkWidget * dialog,int width_id,int height_id,bool resizeable)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 
ShowDialogWithMinLocalizedWidth(GtkWidget * dialog,int width_id)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 
PresentWindow(GtkWidget * window,int timestamp)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 
GetDialogWindow(GtkWidget * dialog)1084 GtkWindow* GetDialogWindow(GtkWidget* dialog) {
1085   return chromeos::GetNativeDialogWindow(dialog);
1086 }
1087 
GetDialogBounds(GtkWidget * dialog)1088 gfx::Rect GetDialogBounds(GtkWidget* dialog) {
1089   return chromeos::GetNativeDialogContentsBounds(dialog);
1090 }
1091 
1092 #else
1093 
ShowDialog(GtkWidget * dialog)1094 void ShowDialog(GtkWidget* dialog) {
1095   gtk_widget_show_all(dialog);
1096 }
1097 
ShowDialogWithLocalizedSize(GtkWidget * dialog,int width_id,int height_id,bool resizeable)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 
ShowDialogWithMinLocalizedWidth(GtkWidget * dialog,int width_id)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 
PresentWindow(GtkWidget * window,int timestamp)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 
GetDialogWindow(GtkWidget * dialog)1130 GtkWindow* GetDialogWindow(GtkWidget* dialog) {
1131   return GTK_WINDOW(dialog);
1132 }
1133 
GetDialogBounds(GtkWidget * dialog)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 
GetStockPreferencesMenuLabel()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 
IsWidgetAncestryVisible(GtkWidget * widget)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 
SetLabelWidth(GtkWidget * label,int pixel_width)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 
InitLabelSizeRequestAndEllipsizeMode(GtkWidget * label)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 
WebDragOpToGdkDragAction(WebDragOperationsMask op)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 
GdkDragActionToWebDragOp(GdkDragAction action)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 
ApplyMessageDialogQuirks(GtkWidget * dialog)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.
DoCutCopyPaste(BrowserWindow * window,void (RenderViewHost::* method)(),const char * signal)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 
DoCut(BrowserWindow * window)1244 void DoCut(BrowserWindow* window) {
1245   DoCutCopyPaste(window, &RenderViewHost::Cut, "cut-clipboard");
1246 }
1247 
DoCopy(BrowserWindow * window)1248 void DoCopy(BrowserWindow* window) {
1249   DoCutCopyPaste(window, &RenderViewHost::Copy, "copy-clipboard");
1250 }
1251 
DoPaste(BrowserWindow * window)1252 void DoPaste(BrowserWindow* window) {
1253   DoCutCopyPaste(window, &RenderViewHost::Paste, "paste-clipboard");
1254 }
1255 
1256 }  // namespace gtk_util
1257