OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/gtk/info_bubble_gtk.h" | 5 #include "chrome/browser/gtk/info_bubble_gtk.h" |
6 | 6 |
7 #include <gdk/gdkkeysyms.h> | 7 #include <gdk/gdkkeysyms.h> |
8 #include <gtk/gtk.h> | 8 #include <gtk/gtk.h> |
9 | 9 |
10 #include "app/l10n_util.h" | 10 #include "app/l10n_util.h" |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63); | 42 const GdkColor kFrameColor = GDK_COLOR_RGB(0x63, 0x63, 0x63); |
43 | 43 |
44 } // namespace | 44 } // namespace |
45 | 45 |
46 // static | 46 // static |
47 InfoBubbleGtk* InfoBubbleGtk::Show(GtkWindow* toplevel_window, | 47 InfoBubbleGtk* InfoBubbleGtk::Show(GtkWindow* toplevel_window, |
48 const gfx::Rect& rect, | 48 const gfx::Rect& rect, |
49 GtkWidget* content, | 49 GtkWidget* content, |
50 ArrowLocationGtk arrow_location, | 50 ArrowLocationGtk arrow_location, |
51 bool match_system_theme, | 51 bool match_system_theme, |
| 52 bool grab_input, |
52 GtkThemeProvider* provider, | 53 GtkThemeProvider* provider, |
53 InfoBubbleGtkDelegate* delegate) { | 54 InfoBubbleGtkDelegate* delegate) { |
54 InfoBubbleGtk* bubble = new InfoBubbleGtk(provider, match_system_theme); | 55 InfoBubbleGtk* bubble = new InfoBubbleGtk(provider, match_system_theme); |
55 bubble->Init(toplevel_window, rect, content, arrow_location); | 56 bubble->Init(toplevel_window, rect, content, arrow_location, grab_input); |
56 bubble->set_delegate(delegate); | 57 bubble->set_delegate(delegate); |
57 return bubble; | 58 return bubble; |
58 } | 59 } |
59 | 60 |
60 InfoBubbleGtk::InfoBubbleGtk(GtkThemeProvider* provider, | 61 InfoBubbleGtk::InfoBubbleGtk(GtkThemeProvider* provider, |
61 bool match_system_theme) | 62 bool match_system_theme) |
62 : delegate_(NULL), | 63 : delegate_(NULL), |
63 window_(NULL), | 64 window_(NULL), |
64 theme_provider_(provider), | 65 theme_provider_(provider), |
65 accel_group_(gtk_accel_group_new()), | 66 accel_group_(gtk_accel_group_new()), |
66 toplevel_window_(NULL), | 67 toplevel_window_(NULL), |
67 mask_region_(NULL), | 68 mask_region_(NULL), |
68 preferred_arrow_location_(ARROW_LOCATION_TOP_LEFT), | 69 preferred_arrow_location_(ARROW_LOCATION_TOP_LEFT), |
69 current_arrow_location_(ARROW_LOCATION_TOP_LEFT), | 70 current_arrow_location_(ARROW_LOCATION_TOP_LEFT), |
70 match_system_theme_(match_system_theme) { | 71 match_system_theme_(match_system_theme), |
| 72 grab_input_(true), |
| 73 closed_by_escape_(false) { |
71 } | 74 } |
72 | 75 |
73 InfoBubbleGtk::~InfoBubbleGtk() { | 76 InfoBubbleGtk::~InfoBubbleGtk() { |
| 77 // Notify the delegate that we're about to close. This gives the chance |
| 78 // to save state / etc from the hosted widget before it's destroyed. |
| 79 if (delegate_) |
| 80 delegate_->InfoBubbleClosing(this, closed_by_escape_); |
| 81 |
74 g_object_unref(accel_group_); | 82 g_object_unref(accel_group_); |
75 if (mask_region_) { | 83 if (mask_region_) { |
76 gdk_region_destroy(mask_region_); | 84 gdk_region_destroy(mask_region_); |
77 mask_region_ = NULL; | 85 mask_region_ = NULL; |
78 } | 86 } |
79 | 87 |
80 g_signal_handlers_disconnect_by_func( | 88 if (toplevel_window_) { |
81 toplevel_window_, | 89 g_signal_handlers_disconnect_by_func( |
82 reinterpret_cast<gpointer>(HandleToplevelConfigureThunk), | 90 toplevel_window_, |
83 this); | 91 reinterpret_cast<gpointer>(HandleToplevelConfigureThunk), |
84 g_signal_handlers_disconnect_by_func( | 92 this); |
85 toplevel_window_, | 93 g_signal_handlers_disconnect_by_func( |
86 reinterpret_cast<gpointer>(HandleToplevelUnmapThunk), | 94 toplevel_window_, |
87 this); | 95 reinterpret_cast<gpointer>(HandleToplevelUnmapThunk), |
| 96 this); |
| 97 } |
88 toplevel_window_ = NULL; | 98 toplevel_window_ = NULL; |
89 } | 99 } |
90 | 100 |
91 void InfoBubbleGtk::Init(GtkWindow* toplevel_window, | 101 void InfoBubbleGtk::Init(GtkWindow* toplevel_window, |
92 const gfx::Rect& rect, | 102 const gfx::Rect& rect, |
93 GtkWidget* content, | 103 GtkWidget* content, |
94 ArrowLocationGtk arrow_location) { | 104 ArrowLocationGtk arrow_location, |
| 105 bool grab_input) { |
95 DCHECK(!window_); | 106 DCHECK(!window_); |
96 toplevel_window_ = toplevel_window; | 107 toplevel_window_ = toplevel_window; |
97 rect_ = rect; | 108 rect_ = rect; |
98 preferred_arrow_location_ = arrow_location; | 109 preferred_arrow_location_ = arrow_location; |
99 | 110 |
100 window_ = gtk_window_new(GTK_WINDOW_POPUP); | 111 grab_input_ = grab_input; |
| 112 // Using a TOPLEVEL window may cause placement issues with certain WMs but it |
| 113 // is necessary to be able to focus the window. |
| 114 window_ = gtk_window_new(grab_input ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); |
| 115 |
101 gtk_widget_set_app_paintable(window_, TRUE); | 116 gtk_widget_set_app_paintable(window_, TRUE); |
102 // Resizing is handled by the program, not user. | 117 // Resizing is handled by the program, not user. |
103 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 118 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
104 | 119 |
105 // Attach our accelerator group to the window with an escape accelerator. | 120 // Attach our accelerator group to the window with an escape accelerator. |
106 gtk_accel_group_connect(accel_group_, GDK_Escape, | 121 gtk_accel_group_connect(accel_group_, GDK_Escape, |
107 static_cast<GdkModifierType>(0), static_cast<GtkAccelFlags>(0), | 122 static_cast<GdkModifierType>(0), static_cast<GtkAccelFlags>(0), |
108 g_cclosure_new(G_CALLBACK(&HandleEscapeThunk), this, NULL)); | 123 g_cclosure_new(G_CALLBACK(&HandleEscapeThunk), this, NULL)); |
109 gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group_); | 124 gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group_); |
110 | 125 |
(...skipping 22 matching lines...) Expand all Loading... |
133 G_CALLBACK(HandleSizeAllocateThunk), this); | 148 G_CALLBACK(HandleSizeAllocateThunk), this); |
134 g_signal_connect(window_, "button-press-event", | 149 g_signal_connect(window_, "button-press-event", |
135 G_CALLBACK(&HandleButtonPressThunk), this); | 150 G_CALLBACK(&HandleButtonPressThunk), this); |
136 g_signal_connect(window_, "destroy", | 151 g_signal_connect(window_, "destroy", |
137 G_CALLBACK(&HandleDestroyThunk), this); | 152 G_CALLBACK(&HandleDestroyThunk), this); |
138 | 153 |
139 g_signal_connect(toplevel_window, "configure-event", | 154 g_signal_connect(toplevel_window, "configure-event", |
140 G_CALLBACK(&HandleToplevelConfigureThunk), this); | 155 G_CALLBACK(&HandleToplevelConfigureThunk), this); |
141 g_signal_connect(toplevel_window, "unmap-event", | 156 g_signal_connect(toplevel_window, "unmap-event", |
142 G_CALLBACK(&HandleToplevelUnmapThunk), this); | 157 G_CALLBACK(&HandleToplevelUnmapThunk), this); |
| 158 // Set |toplevel_window_| to NULL if it gets destroyed. |
| 159 g_signal_connect(toplevel_window, "destroy", |
| 160 G_CALLBACK(gtk_widget_destroyed), &toplevel_window_); |
143 | 161 |
144 gtk_widget_show_all(window_); | 162 gtk_widget_show_all(window_); |
145 | 163 |
146 // We add a GTK (application-level) grab. This means we will get all | 164 if (grab_input_) { |
147 // mouse events for our application, even if they were delivered on another | 165 // We add a GTK (application-level) grab. This means we will get all |
148 // window. We don't need this to get button presses outside of the bubble's | 166 // mouse events for our application, even if they were delivered on another |
149 // window so we'll know to close it (the pointer grab takes care of that), but | 167 // window. We don't need this to get button presses outside of the bubble's |
150 // it prevents other widgets from getting highlighted when the pointer moves | 168 // window so we'll know to close it (the pointer grab takes care of that), |
151 // over them. | 169 // but it prevents other widgets from getting highlighted when the pointer |
152 // | 170 // moves over them. |
153 // (Ideally we wouldn't add the window to a group and it would just get all | 171 // |
154 // the mouse events, but gtk_grab_add() doesn't appear to do anything in that | 172 // (Ideally we wouldn't add the window to a group and it would just get all |
155 // case. Adding it to the toplevel window's group first appears to block | 173 // the mouse events, but gtk_grab_add() doesn't appear to do anything in |
156 // enter/leave events for that window and its subwindows, although other | 174 // that case. Adding it to the toplevel window's group first appears to |
157 // browser windows still receive them). | 175 // block enter/leave events for that window and its subwindows, although |
158 gtk_window_group_add_window(gtk_window_get_group(toplevel_window), | 176 // other browser windows still receive them). |
159 GTK_WINDOW(window_)); | 177 gtk_window_group_add_window(gtk_window_get_group(toplevel_window), |
160 gtk_grab_add(window_); | 178 GTK_WINDOW(window_)); |
| 179 gtk_grab_add(window_); |
161 | 180 |
162 GrabPointerAndKeyboard(); | 181 GrabPointerAndKeyboard(); |
| 182 } |
163 | 183 |
164 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, | 184 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
165 NotificationService::AllSources()); | 185 NotificationService::AllSources()); |
166 theme_provider_->InitThemesFor(this); | 186 theme_provider_->InitThemesFor(this); |
167 } | 187 } |
168 | 188 |
169 // NOTE: This seems a bit overcomplicated, but it requires a bunch of careful | 189 // NOTE: This seems a bit overcomplicated, but it requires a bunch of careful |
170 // fudging to get the pixels rasterized exactly where we want them, the arrow to | 190 // fudging to get the pixels rasterized exactly where we want them, the arrow to |
171 // have a 1 pixel point, etc. | 191 // have a 1 pixel point, etc. |
172 // TODO(deanm): Windows draws with Skia and uses some PNG images for the | 192 // TODO(deanm): Windows draws with Skia and uses some PNG images for the |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 DCHECK_EQ(type.value, NotificationType::BROWSER_THEME_CHANGED); | 334 DCHECK_EQ(type.value, NotificationType::BROWSER_THEME_CHANGED); |
315 if (theme_provider_->UseGtkTheme() && match_system_theme_) { | 335 if (theme_provider_->UseGtkTheme() && match_system_theme_) { |
316 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, NULL); | 336 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, NULL); |
317 } else { | 337 } else { |
318 // Set the background color, so we don't need to paint it manually. | 338 // Set the background color, so we don't need to paint it manually. |
319 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &kBackgroundColor); | 339 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &kBackgroundColor); |
320 } | 340 } |
321 } | 341 } |
322 | 342 |
323 void InfoBubbleGtk::HandlePointerAndKeyboardUngrabbedByContent() { | 343 void InfoBubbleGtk::HandlePointerAndKeyboardUngrabbedByContent() { |
324 GrabPointerAndKeyboard(); | 344 if (grab_input_) |
| 345 GrabPointerAndKeyboard(); |
325 } | 346 } |
326 | 347 |
327 void InfoBubbleGtk::CloseInternal(bool closed_by_escape) { | 348 void InfoBubbleGtk::Close() { |
328 // Notify the delegate that we're about to close. This gives the chance | |
329 // to save state / etc from the hosted widget before it's destroyed. | |
330 if (delegate_) | |
331 delegate_->InfoBubbleClosing(this, closed_by_escape); | |
332 | |
333 // We don't need to ungrab the pointer or keyboard here; the X server will | 349 // We don't need to ungrab the pointer or keyboard here; the X server will |
334 // automatically do that when we destroy our window. | 350 // automatically do that when we destroy our window. |
335 | |
336 DCHECK(window_); | 351 DCHECK(window_); |
337 gtk_widget_destroy(window_); | 352 gtk_widget_destroy(window_); |
338 // |this| has been deleted, see HandleDestroy. | 353 // |this| has been deleted, see HandleDestroy. |
339 } | 354 } |
340 | 355 |
341 void InfoBubbleGtk::GrabPointerAndKeyboard() { | 356 void InfoBubbleGtk::GrabPointerAndKeyboard() { |
342 // Install X pointer and keyboard grabs to make sure that we have the focus | 357 // Install X pointer and keyboard grabs to make sure that we have the focus |
343 // and get all mouse and keyboard events until we're closed. | 358 // and get all mouse and keyboard events until we're closed. |
344 GdkGrabStatus pointer_grab_status = | 359 GdkGrabStatus pointer_grab_status = |
345 gdk_pointer_grab(window_->window, | 360 gdk_pointer_grab(window_->window, |
(...skipping 12 matching lines...) Expand all Loading... |
358 gdk_keyboard_grab(window_->window, | 373 gdk_keyboard_grab(window_->window, |
359 FALSE, // owner_events | 374 FALSE, // owner_events |
360 GDK_CURRENT_TIME); | 375 GDK_CURRENT_TIME); |
361 if (keyboard_grab_status != GDK_GRAB_SUCCESS) { | 376 if (keyboard_grab_status != GDK_GRAB_SUCCESS) { |
362 DLOG(ERROR) << "Unable to grab keyboard (status=" | 377 DLOG(ERROR) << "Unable to grab keyboard (status=" |
363 << keyboard_grab_status << ")"; | 378 << keyboard_grab_status << ")"; |
364 } | 379 } |
365 } | 380 } |
366 | 381 |
367 gboolean InfoBubbleGtk::HandleEscape() { | 382 gboolean InfoBubbleGtk::HandleEscape() { |
368 CloseInternal(true); // Close by escape. | 383 closed_by_escape_ = true; |
| 384 Close(); |
369 return TRUE; | 385 return TRUE; |
370 } | 386 } |
371 | 387 |
372 gboolean InfoBubbleGtk::HandleExpose() { | 388 gboolean InfoBubbleGtk::HandleExpose() { |
373 GdkDrawable* drawable = GDK_DRAWABLE(window_->window); | 389 GdkDrawable* drawable = GDK_DRAWABLE(window_->window); |
374 GdkGC* gc = gdk_gc_new(drawable); | 390 GdkGC* gc = gdk_gc_new(drawable); |
375 gdk_gc_set_rgb_fg_color(gc, &kFrameColor); | 391 gdk_gc_set_rgb_fg_color(gc, &kFrameColor); |
376 | 392 |
377 // Stroke the frame border. | 393 // Stroke the frame border. |
378 std::vector<GdkPoint> points = MakeFramePolygonPoints( | 394 std::vector<GdkPoint> points = MakeFramePolygonPoints( |
(...skipping 25 matching lines...) Expand all Loading... |
404 (mask_region_ && gdk_region_point_in(mask_region_, event->x, event->y))) { | 420 (mask_region_ && gdk_region_point_in(mask_region_, event->x, event->y))) { |
405 return FALSE; // Propagate. | 421 return FALSE; // Propagate. |
406 } | 422 } |
407 | 423 |
408 // Our content widget got a click. | 424 // Our content widget got a click. |
409 if (event->window != window_->window && | 425 if (event->window != window_->window && |
410 gdk_window_get_toplevel(event->window) == window_->window) { | 426 gdk_window_get_toplevel(event->window) == window_->window) { |
411 return FALSE; | 427 return FALSE; |
412 } | 428 } |
413 | 429 |
414 // Otherwise we had a click outside of our window, close ourself. | 430 if (grab_input_) { |
415 Close(); | 431 // Otherwise we had a click outside of our window, close ourself. |
416 return TRUE; | 432 Close(); |
| 433 return TRUE; |
| 434 } |
| 435 |
| 436 return FALSE; |
417 } | 437 } |
418 | 438 |
419 gboolean InfoBubbleGtk::HandleDestroy() { | 439 gboolean InfoBubbleGtk::HandleDestroy() { |
420 // We are self deleting, we have a destroy signal setup to catch when we | 440 // We are self deleting, we have a destroy signal setup to catch when we |
421 // destroy the widget manually, or the window was closed via X. This will | 441 // destroy the widget manually, or the window was closed via X. This will |
422 // delete the InfoBubbleGtk object. | 442 // delete the InfoBubbleGtk object. |
423 delete this; | 443 delete this; |
424 return FALSE; // Propagate. | 444 return FALSE; // Propagate. |
425 } | 445 } |
426 | 446 |
427 gboolean InfoBubbleGtk::HandleToplevelConfigure(GdkEventConfigure* event) { | 447 gboolean InfoBubbleGtk::HandleToplevelConfigure(GdkEventConfigure* event) { |
428 if (!UpdateArrowLocation(false)) | 448 if (!UpdateArrowLocation(false)) |
429 MoveWindow(); | 449 MoveWindow(); |
430 StackWindow(); | 450 StackWindow(); |
431 return FALSE; | 451 return FALSE; |
432 } | 452 } |
433 | 453 |
434 gboolean InfoBubbleGtk::HandleToplevelUnmap() { | 454 gboolean InfoBubbleGtk::HandleToplevelUnmap() { |
435 Close(); | 455 Close(); |
436 return FALSE; | 456 return FALSE; |
437 } | 457 } |
OLD | NEW |