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