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