Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(621)

Side by Side Diff: chrome/browser/ui/panels/panel_gtk.cc

Issue 10831226: Panels refactor: Support browserless panels on Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove static initializer Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/panels/panel_gtk.h ('k') | chrome/browser/ui/panels/panel_host.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/ui/panels/panel_gtk.h"
6
7 #include <gdk/gdk.h>
8 #include <gdk/gdkkeysyms.h>
9 #include <X11/XF86keysym.h>
10
11 #include "base/bind.h"
12 #include "base/debug/trace_event.h"
5 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/message_loop.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/app/chrome_command_ids.h"
17 #include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog_queue.h"
18 #include "chrome/browser/ui/gtk/custom_button.h"
19 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
20 #include "chrome/browser/ui/gtk/gtk_util.h"
21 #include "chrome/browser/ui/gtk/gtk_window_util.h"
6 #include "chrome/browser/ui/panels/panel.h" 22 #include "chrome/browser/ui/panels/panel.h"
23 #include "chrome/browser/ui/panels/panel_bounds_animation.h"
24 #include "chrome/browser/ui/panels/panel_titlebar_gtk.h"
25 #include "chrome/browser/ui/panels/panel_constants.h"
26 #include "chrome/browser/ui/panels/panel_drag_gtk.h"
27 #include "chrome/browser/ui/panels/panel_manager.h"
28 #include "chrome/browser/web_applications/web_app.h"
29 #include "chrome/common/chrome_notification_types.h"
30 #include "content/public/browser/native_web_keyboard_event.h"
31 #include "content/public/browser/notification_service.h"
32 #include "content/public/browser/web_contents.h"
33 #include "grit/theme_resources.h"
34 #include "grit/ui_resources.h"
35 #include "ui/base/accelerators/accelerator_gtk.h"
36 #include "ui/base/gtk/gtk_compat.h"
37 #include "ui/base/gtk/gtk_expanded_container.h"
38 #include "ui/base/gtk/gtk_hig_constants.h"
39 #include "ui/base/x/active_window_watcher_x.h"
40 #include "ui/gfx/canvas.h"
41 #include "ui/gfx/image/cairo_cached_surface.h"
42 #include "ui/gfx/image/image.h"
43
44 using content::NativeWebKeyboardEvent;
45 using content::WebContents;
46
47 namespace {
48
49 const char* kPanelWindowKey = "__PANEL_GTK__";
50
51 // The number of milliseconds between loading animation frames.
52 const int kLoadingAnimationFrameTimeMs = 30;
53
54 // The frame border is only visible in restored mode and is hardcoded to 4 px
55 // on each side regardless of the system window border size.
56 const int kFrameBorderThickness = 4;
57 // While resize areas on Windows are normally the same size as the window
58 // borders, our top area is shrunk by 1 px to make it easier to move the window
59 // around with our thinner top grabbable strip. (Incidentally, our side and
60 // bottom resize areas don't match the frame border thickness either -- they
61 // span the whole nonclient area, so there's no "dead zone" for the mouse.)
62 const int kTopResizeAdjust = 1;
63 // In the window corners, the resize areas don't actually expand bigger, but
64 // the 16 px at the end of each edge triggers diagonal resizing.
65 const int kResizeAreaCornerSize = 16;
66
67 // Colors used to draw frame background under default theme.
68 const SkColor kActiveBackgroundDefaultColor = SkColorSetRGB(0x3a, 0x3d, 0x3d);
69 const SkColor kInactiveBackgroundDefaultColor = SkColorSetRGB(0x7a, 0x7c, 0x7c);
70 const SkColor kAttentionBackgroundDefaultColor =
71 SkColorSetRGB(0xff, 0xab, 0x57);
72 const SkColor kMinimizeBackgroundDefaultColor = SkColorSetRGB(0xf5, 0xf4, 0xf0);
73 const SkColor kMinimizeBorderDefaultColor = SkColorSetRGB(0xc9, 0xc9, 0xc9);
74
75 // Color used to draw the divider line between the titlebar and the client area.
76 const SkColor kDividerColor = SkColorSetRGB(0x2a, 0x2c, 0x2c);
77
78 // Set minimium width for window really small.
79 const int kMinWindowWidth = 26;
80
81 // Table of supported accelerators in Panels.
82 const struct AcceleratorMapping {
83 guint keyval;
84 int command_id;
85 GdkModifierType modifier_type;
86 } kAcceleratorMap[] = {
87 // Window controls.
88 { GDK_w, IDC_CLOSE_WINDOW, GDK_CONTROL_MASK },
89 { GDK_w, IDC_CLOSE_WINDOW,
90 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) },
91 { GDK_q, IDC_EXIT, GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) },
92
93 // Zoom level.
94 { GDK_KP_Add, IDC_ZOOM_PLUS, GDK_CONTROL_MASK },
95 { GDK_plus, IDC_ZOOM_PLUS,
96 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) },
97 { GDK_equal, IDC_ZOOM_PLUS, GDK_CONTROL_MASK },
98 { XF86XK_ZoomIn, IDC_ZOOM_PLUS, GdkModifierType(0) },
99 { GDK_KP_0, IDC_ZOOM_NORMAL, GDK_CONTROL_MASK },
100 { GDK_0, IDC_ZOOM_NORMAL, GDK_CONTROL_MASK },
101 { GDK_KP_Subtract, IDC_ZOOM_MINUS, GDK_CONTROL_MASK },
102 { GDK_minus, IDC_ZOOM_MINUS, GDK_CONTROL_MASK },
103 { GDK_underscore, IDC_ZOOM_MINUS,
104 GdkModifierType(GDK_CONTROL_MASK | GDK_SHIFT_MASK) },
105 { XF86XK_ZoomOut, IDC_ZOOM_MINUS, GdkModifierType(0) },
106
107 // Navigation.
108 { GDK_Escape, IDC_STOP, GdkModifierType(0) },
109 { XF86XK_Stop, IDC_STOP, GdkModifierType(0) },
110 { GDK_r, IDC_RELOAD, GDK_CONTROL_MASK },
111 { GDK_r, IDC_RELOAD_IGNORING_CACHE,
112 GdkModifierType(GDK_CONTROL_MASK|GDK_SHIFT_MASK) },
113 { GDK_F5, IDC_RELOAD, GdkModifierType(0) },
114 { GDK_F5, IDC_RELOAD_IGNORING_CACHE, GDK_CONTROL_MASK },
115 { GDK_F5, IDC_RELOAD_IGNORING_CACHE, GDK_SHIFT_MASK },
116 { XF86XK_Reload, IDC_RELOAD, GdkModifierType(0) },
117 { XF86XK_Refresh, IDC_RELOAD, GdkModifierType(0) },
118
119 // Editing.
120 { GDK_c, IDC_COPY, GDK_CONTROL_MASK },
121 { GDK_x, IDC_CUT, GDK_CONTROL_MASK },
122 { GDK_v, IDC_PASTE, GDK_CONTROL_MASK },
123 };
124
125 // Table of accelerator mappings to command ids.
126 typedef std::map<ui::AcceleratorGtk, int> AcceleratorGtkMap;
127
128 const AcceleratorGtkMap& GetAcceleratorTable() {
129 CR_DEFINE_STATIC_LOCAL(AcceleratorGtkMap, accelerator_table, ());
130 if (accelerator_table.empty()) {
131 for (size_t i = 0; i < arraysize(kAcceleratorMap); ++i) {
132 const AcceleratorMapping& entry = kAcceleratorMap[i];
133 ui::AcceleratorGtk accelerator(entry.keyval, entry.modifier_type);
134 accelerator_table[accelerator] = entry.command_id;
135 }
136 }
137 return accelerator_table;
138 }
139
140 gfx::Image* CreateImageForColor(SkColor color) {
141 gfx::Canvas canvas(gfx::Size(1, 1), ui::SCALE_FACTOR_100P, true);
142 canvas.DrawColor(color);
143 return new gfx::Image(gfx::ImageSkia(canvas.ExtractImageRep()));
144 }
145
146 const gfx::Image* GetActiveBackgroundDefaultImage() {
147 static gfx::Image* image = NULL;
148 if (!image)
149 image = CreateImageForColor(kActiveBackgroundDefaultColor);
150 return image;
151 }
152
153 const gfx::Image* GetInactiveBackgroundDefaultImage() {
154 static gfx::Image* image = NULL;
155 if (!image)
156 image = CreateImageForColor(kInactiveBackgroundDefaultColor);
157 return image;
158 }
159
160 const gfx::Image* GetAttentionBackgroundDefaultImage() {
161 static gfx::Image* image = NULL;
162 if (!image)
163 image = CreateImageForColor(kAttentionBackgroundDefaultColor);
164 return image;
165 }
166
167 const gfx::Image* GetMinimizeBackgroundDefaultImage() {
168 static gfx::Image* image = NULL;
169 if (!image)
170 image = CreateImageForColor(kMinimizeBackgroundDefaultColor);
171 return image;
172 }
173
174 // Used to stash a pointer to the Panel window inside the native
175 // Gtk window for retrieval in static callbacks.
176 GQuark GetPanelWindowQuarkKey() {
177 static GQuark quark = g_quark_from_static_string(kPanelWindowKey);
178 return quark;
179 }
180
181 // Size of window frame. Empty until first panel has been allocated
182 // and sized. Frame size won't change for other panels so it can be
183 // computed once for all panels.
184 gfx::Size& GetFrameSize() {
185 CR_DEFINE_STATIC_LOCAL(gfx::Size, frame_size, ());
186 return frame_size;
187 }
188
189 void SetFrameSize(const gfx::Size& new_size) {
190 gfx::Size& frame_size = GetFrameSize();
191 frame_size.SetSize(new_size.width(), new_size.height());
192 }
193
194 }
7 195
8 // static 196 // static
9 NativePanel* Panel::CreateNativePanel(Panel* panel, const gfx::Rect& bounds) { 197 NativePanel* Panel::CreateNativePanel(Panel* panel, const gfx::Rect& bounds) {
10 NOTIMPLEMENTED(); 198 PanelGtk* panel_gtk = new PanelGtk(panel, bounds);
11 return NULL; 199 panel_gtk->Init();
12 } 200 return panel_gtk;
201 }
202
203 PanelGtk::PanelGtk(Panel* panel, const gfx::Rect& bounds)
204 : panel_(panel),
205 bounds_(bounds),
206 is_shown_(false),
207 paint_state_(PAINT_AS_INACTIVE),
208 is_drawing_attention_(false),
209 frame_cursor_(NULL),
210 is_active_(!ui::ActiveWindowWatcherX::WMSupportsActivation()),
211 window_(NULL),
212 window_container_(NULL),
213 window_vbox_(NULL),
214 render_area_event_box_(NULL),
215 contents_expanded_(NULL),
216 accel_group_(NULL) {
217 }
218
219 PanelGtk::~PanelGtk() {
220 ui::ActiveWindowWatcherX::RemoveObserver(this);
221 }
222
223 void PanelGtk::Init() {
224 ui::ActiveWindowWatcherX::AddObserver(this);
225
226 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
227 g_object_set_qdata(G_OBJECT(window_), GetPanelWindowQuarkKey(), this);
228 gtk_widget_add_events(GTK_WIDGET(window_), GDK_BUTTON_PRESS_MASK |
229 GDK_POINTER_MOTION_MASK);
230 gtk_window_set_decorated(window_, false);
231 // Keep the window always on top.
232 gtk_window_set_keep_above(window_, TRUE);
233 // Show the window on all the virtual desktops.
234 gtk_window_stick(window_);
235 // Do not show an icon in the task bar. Window operations such as close,
236 // minimize etc. can only be done from the panel UI.
237 gtk_window_set_skip_taskbar_hint(window_, TRUE);
238
239 // Disable the resize gripper on Ubuntu.
240 gtk_window_util::DisableResizeGrip(window_);
241
242 // Add this window to its own unique window group to allow for
243 // window-to-parent modality.
244 gtk_window_group_add_window(gtk_window_group_new(), window_);
245 g_object_unref(gtk_window_get_group(window_));
246
247 // Set minimum height for the window.
248 GdkGeometry hints;
249 hints.min_height = panel::kMinimizedPanelHeight;
250 hints.min_width = kMinWindowWidth;
251 gtk_window_set_geometry_hints(
252 window_, GTK_WIDGET(window_), &hints, GDK_HINT_MIN_SIZE);
253
254 // Connect signal handlers to the window.
255 g_signal_connect(window_, "delete-event",
256 G_CALLBACK(OnMainWindowDeleteEventThunk), this);
257 g_signal_connect(window_, "destroy",
258 G_CALLBACK(OnMainWindowDestroyThunk), this);
259 g_signal_connect(window_, "configure-event",
260 G_CALLBACK(OnConfigureThunk), this);
261 g_signal_connect(window_, "key-press-event",
262 G_CALLBACK(OnKeyPressThunk), this);
263 g_signal_connect(window_, "motion-notify-event",
264 G_CALLBACK(OnMouseMoveEventThunk), this);
265 g_signal_connect(window_, "button-press-event",
266 G_CALLBACK(OnButtonPressEventThunk), this);
267
268 // This vbox contains the titlebar and the render area, but not
269 // the custom frame border.
270 window_vbox_ = gtk_vbox_new(FALSE, 0);
271 gtk_widget_show(window_vbox_);
272
273 // TODO(jennb): add GlobalMenuBar after refactoring out Browser.
274
275 // The window container draws the custom browser frame.
276 window_container_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
277 gtk_widget_set_name(window_container_, "chrome-custom-frame-border");
278 gtk_widget_set_app_paintable(window_container_, TRUE);
279 gtk_widget_set_double_buffered(window_container_, FALSE);
280 gtk_widget_set_redraw_on_allocate(window_container_, TRUE);
281 gtk_alignment_set_padding(GTK_ALIGNMENT(window_container_), 1,
282 kFrameBorderThickness, kFrameBorderThickness, kFrameBorderThickness);
283 g_signal_connect(window_container_, "expose-event",
284 G_CALLBACK(OnCustomFrameExposeThunk), this);
285 gtk_container_add(GTK_CONTAINER(window_container_), window_vbox_);
286
287 // Build the titlebar.
288 titlebar_.reset(new PanelTitlebarGtk(this));
289 titlebar_->Init();
290 gtk_box_pack_start(GTK_BOX(window_vbox_), titlebar_->widget(), FALSE, FALSE,
291 0);
292 g_signal_connect(titlebar_->widget(), "button-press-event",
293 G_CALLBACK(OnTitlebarButtonPressEventThunk), this);
294 g_signal_connect(titlebar_->widget(), "button-release-event",
295 G_CALLBACK(OnTitlebarButtonReleaseEventThunk), this);
296
297 contents_expanded_ = gtk_expanded_container_new();
298 gtk_widget_show(contents_expanded_);
299
300 render_area_event_box_ = gtk_event_box_new();
301 // Set a white background so during startup the user sees white in the
302 // content area before we get a WebContents in place.
303 gtk_widget_modify_bg(render_area_event_box_, GTK_STATE_NORMAL,
304 &ui::kGdkWhite);
305 gtk_container_add(GTK_CONTAINER(render_area_event_box_),
306 contents_expanded_);
307 gtk_widget_show(render_area_event_box_);
308 gtk_box_pack_end(GTK_BOX(window_vbox_), render_area_event_box_,
309 TRUE, TRUE, 0);
310
311 gtk_container_add(GTK_CONTAINER(window_), window_container_);
312 gtk_widget_show(window_container_);
313
314 ConnectAccelerators();
315 }
316
317 void PanelGtk::UpdateWindowShape(int width, int height) {
318 // For panels, only top corners are rounded. The bottom corners are not
319 // rounded because panels are aligned to the bottom edge of the screen.
320 GdkRectangle top_top_rect = { 3, 0, width - 6, 1 };
321 GdkRectangle top_mid_rect = { 1, 1, width - 2, 2 };
322 GdkRectangle mid_rect = { 0, 3, width, height - 3 };
323 GdkRegion* mask = gdk_region_rectangle(&top_top_rect);
324 gdk_region_union_with_rect(mask, &top_mid_rect);
325 gdk_region_union_with_rect(mask, &mid_rect);
326 gdk_window_shape_combine_region(
327 gtk_widget_get_window(GTK_WIDGET(window_)), mask, 0, 0);
328 if (mask)
329 gdk_region_destroy(mask);
330 }
331
332 gboolean PanelGtk::OnConfigure(GtkWidget* widget,
333 GdkEventConfigure* event) {
334 // When the window moves, we'll get multiple configure-event signals. We can
335 // also get events when the bounds haven't changed, but the window's stacking
336 // has, which we aren't interested in. http://crbug.com/70125
337 gfx::Size new_size(event->width, event->height);
338 if (new_size == configure_size_)
339 return FALSE;
340
341 UpdateWindowShape(event->width, event->height);
342 configure_size_ = new_size;
343
344 if (!GetFrameSize().IsEmpty())
345 return FALSE;
346
347 // Save the frame size allocated by the system after as the
348 // frame size will be affected when we shrink the panel smaller
349 // than the frame (e.g. when the panel is minimized).
350 SetFrameSize(GetNonClientFrameSize());
351 panel_->OnWindowSizeAvailable();
352
353 content::NotificationService::current()->Notify(
354 chrome::NOTIFICATION_PANEL_WINDOW_SIZE_KNOWN,
355 content::Source<Panel>(panel_.get()),
356 content::NotificationService::NoDetails());
357
358 return FALSE;
359 }
360
361 void PanelGtk::ConnectAccelerators() {
362 accel_group_ = gtk_accel_group_new();
363 gtk_window_add_accel_group(window_, accel_group_);
364
365 const AcceleratorGtkMap& accelerator_table = GetAcceleratorTable();
366 for (AcceleratorGtkMap::const_iterator iter = accelerator_table.begin();
367 iter != accelerator_table.end(); ++iter) {
368 gtk_accel_group_connect(
369 accel_group_,
370 iter->first.GetGdkKeyCode(),
371 static_cast<GdkModifierType>(iter->first.modifiers()),
372 GtkAccelFlags(0),
373 g_cclosure_new(G_CALLBACK(OnGtkAccelerator),
374 GINT_TO_POINTER(iter->second), NULL));
375 }
376 }
377
378 void PanelGtk::DisconnectAccelerators() {
379 // Disconnecting the keys we connected to our accelerator group frees the
380 // closures allocated in ConnectAccelerators.
381 const AcceleratorGtkMap& accelerator_table = GetAcceleratorTable();
382 for (AcceleratorGtkMap::const_iterator iter = accelerator_table.begin();
383 iter != accelerator_table.end(); ++iter) {
384 gtk_accel_group_disconnect_key(accel_group_,
385 iter->first.GetGdkKeyCode(),
386 static_cast<GdkModifierType>(iter->first.modifiers()));
387 }
388 gtk_window_remove_accel_group(window_, accel_group_);
389 g_object_unref(accel_group_);
390 accel_group_ = NULL;
391 }
392
393 // static
394 gboolean PanelGtk::OnGtkAccelerator(GtkAccelGroup* accel_group,
395 GObject* acceleratable,
396 guint keyval,
397 GdkModifierType modifier,
398 void* user_data) {
399 DCHECK(acceleratable);
400 int command_id = GPOINTER_TO_INT(user_data);
401 PanelGtk* panel_gtk = static_cast<PanelGtk*>(
402 g_object_get_qdata(acceleratable, GetPanelWindowQuarkKey()));
403 return panel_gtk->panel()->ExecuteCommandIfEnabled(command_id);
404 }
405
406 gboolean PanelGtk::OnKeyPress(GtkWidget* widget, GdkEventKey* event) {
407 // No way to deactivate a window in GTK, so ignore input if window
408 // is supposed to be 'inactive'. See comments in DeactivatePanel().
409 if (!is_active_)
410 return TRUE;
411
412 // Propagate the key event to child widget first, so we don't override
413 // their accelerators.
414 if (!gtk_window_propagate_key_event(GTK_WINDOW(widget), event)) {
415 if (!gtk_window_activate_key(GTK_WINDOW(widget), event)) {
416 gtk_bindings_activate_event(GTK_OBJECT(widget), event);
417 }
418 }
419 return TRUE;
420 }
421
422 bool PanelGtk::UsingDefaultTheme() const {
423 // No theme is provided for attention painting.
424 if (paint_state_ == PAINT_FOR_ATTENTION)
425 return true;
426
427 GtkThemeService* theme_provider = GtkThemeService::GetFrom(panel_->profile());
428 return theme_provider->UsingDefaultTheme() ||
429 theme_provider->UsingNativeTheme();
430 }
431
432 bool PanelGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) const {
433 // Only detect the window edge when panels can be resized by the user.
434 // This method is used by the base class to detect when the cursor has
435 // hit the window edge in order to change the cursor to a resize cursor
436 // and to detect when to initiate a resize drag.
437 panel::Resizability resizability = panel_->CanResizeByMouse();
438 if (panel::NOT_RESIZABLE == resizability)
439 return false;
440
441 if (x < kFrameBorderThickness) {
442 // Left edge.
443 if (y < kResizeAreaCornerSize - kTopResizeAdjust) {
444 *edge = GDK_WINDOW_EDGE_NORTH_WEST;
445 } else if (y < bounds_.height() - kResizeAreaCornerSize) {
446 *edge = GDK_WINDOW_EDGE_WEST;
447 } else {
448 *edge = GDK_WINDOW_EDGE_SOUTH_WEST;
449 }
450 } else if (x < bounds_.width() - kFrameBorderThickness) {
451 if (y < kFrameBorderThickness - kTopResizeAdjust) {
452 // Top edge.
453 if (x < kResizeAreaCornerSize) {
454 *edge = GDK_WINDOW_EDGE_NORTH_WEST;
455 } else if (x < bounds_.width() - kResizeAreaCornerSize) {
456 *edge = GDK_WINDOW_EDGE_NORTH;
457 } else {
458 *edge = GDK_WINDOW_EDGE_NORTH_EAST;
459 }
460 } else if (y < bounds_.height() - kFrameBorderThickness) {
461 // Ignore the middle content area.
462 return false;
463 } else {
464 // Bottom edge.
465 if (x < kResizeAreaCornerSize) {
466 *edge = GDK_WINDOW_EDGE_SOUTH_WEST;
467 } else if (x < bounds_.width() - kResizeAreaCornerSize) {
468 *edge = GDK_WINDOW_EDGE_SOUTH;
469 } else {
470 *edge = GDK_WINDOW_EDGE_SOUTH_EAST;
471 }
472 }
473 } else {
474 // Right edge.
475 if (y < kResizeAreaCornerSize - kTopResizeAdjust) {
476 *edge = GDK_WINDOW_EDGE_NORTH_EAST;
477 } else if (y < bounds_.height() - kResizeAreaCornerSize) {
478 *edge = GDK_WINDOW_EDGE_EAST;
479 } else {
480 *edge = GDK_WINDOW_EDGE_SOUTH_EAST;
481 }
482 }
483
484 // Special handling if bottom edge is not resizable.
485 if (panel::RESIZABLE_ALL_SIDES_EXCEPT_BOTTOM == resizability) {
486 if (*edge == GDK_WINDOW_EDGE_SOUTH)
487 return FALSE;
488 if (*edge == GDK_WINDOW_EDGE_SOUTH_WEST)
489 *edge = GDK_WINDOW_EDGE_WEST;
490 else if (*edge == GDK_WINDOW_EDGE_SOUTH_EAST)
491 *edge = GDK_WINDOW_EDGE_EAST;
492 }
493
494 return true;
495 }
496
497 const gfx::Image* PanelGtk::GetFrameBackground() const {
498 return UsingDefaultTheme() ?
499 GetDefaultFrameBackground() : GetThemedFrameBackground();
500 }
501
502 const gfx::Image* PanelGtk::GetDefaultFrameBackground() const {
503 switch (paint_state_) {
504 case PAINT_AS_INACTIVE:
505 return GetInactiveBackgroundDefaultImage();
506 case PAINT_AS_ACTIVE:
507 return GetActiveBackgroundDefaultImage();
508 case PAINT_AS_MINIMIZED:
509 return GetMinimizeBackgroundDefaultImage();
510 case PAINT_FOR_ATTENTION:
511 return GetAttentionBackgroundDefaultImage();
512 default:
513 NOTREACHED();
514 return GetInactiveBackgroundDefaultImage();
515 }
516 }
517
518 const gfx::Image* PanelGtk::GetThemedFrameBackground() const {
519 GtkThemeService* theme_provider = GtkThemeService::GetFrom(panel_->profile());
520 return theme_provider->GetImageNamed(paint_state_ == PAINT_AS_ACTIVE ?
521 IDR_THEME_TOOLBAR : IDR_THEME_TAB_BACKGROUND);
522 }
523
524 gboolean PanelGtk::OnCustomFrameExpose(GtkWidget* widget,
525 GdkEventExpose* event) {
526 TRACE_EVENT0("ui::gtk", "PanelGtk::OnCustomFrameExpose");
527 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget));
528 gdk_cairo_rectangle(cr, &event->area);
529 cairo_clip(cr);
530
531 // Update the painting state.
532 int window_height = gdk_window_get_height(gtk_widget_get_window(widget));
533 if (is_drawing_attention_)
534 paint_state_ = PAINT_FOR_ATTENTION;
535 else if (window_height <= panel::kMinimizedPanelHeight)
536 paint_state_ = PAINT_AS_MINIMIZED;
537 else if (is_active_)
538 paint_state_ = PAINT_AS_ACTIVE;
539 else
540 paint_state_ = PAINT_AS_INACTIVE;
541
542 // Draw the background.
543 gfx::CairoCachedSurface* surface = GetFrameBackground()->ToCairo();
544 surface->SetSource(cr, widget, 0, 0);
545 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
546 cairo_rectangle(cr, event->area.x, event->area.y,
547 event->area.width, event->area.height);
548 cairo_fill(cr);
549
550 // Draw the divider only if we're showing more than titlebar.
551 if (window_height > panel::kTitlebarHeight) {
552 cairo_set_source_rgb(cr,
553 SkColorGetR(kDividerColor) / 255.0,
554 SkColorGetG(kDividerColor) / 255.0,
555 SkColorGetB(kDividerColor) / 255.0);
556 cairo_rectangle(cr, 0, panel::kTitlebarHeight - 1, bounds_.width(), 1);
557 cairo_fill(cr);
558 }
559
560 // Draw the border for the minimized panel only.
561 if (paint_state_ == PAINT_AS_MINIMIZED) {
562 cairo_move_to(cr, 0, 3);
563 cairo_line_to(cr, 1, 2);
564 cairo_line_to(cr, 1, 1);
565 cairo_line_to(cr, 2, 1);
566 cairo_line_to(cr, 3, 0);
567 cairo_line_to(cr, event->area.width - 3, 0);
568 cairo_line_to(cr, event->area.width - 2, 1);
569 cairo_line_to(cr, event->area.width - 1, 1);
570 cairo_line_to(cr, event->area.width - 1, 2);
571 cairo_line_to(cr, event->area.width - 1, 3);
572 cairo_line_to(cr, event->area.width - 1, event->area.height - 1);
573 cairo_line_to(cr, 0, event->area.height - 1);
574 cairo_close_path(cr);
575 cairo_set_source_rgb(cr,
576 SkColorGetR(kMinimizeBorderDefaultColor) / 255.0,
577 SkColorGetG(kMinimizeBorderDefaultColor) / 255.0,
578 SkColorGetB(kMinimizeBorderDefaultColor) / 255.0);
579 cairo_set_line_width(cr, 1.0);
580 cairo_stroke(cr);
581 }
582
583 cairo_destroy(cr);
584
585 return FALSE; // Allow subwidgets to paint.
586 }
587
588 void PanelGtk::EnsureDragHelperCreated() {
589 if (drag_helper_.get())
590 return;
591
592 drag_helper_.reset(new PanelDragGtk(panel_.get()));
593 gtk_box_pack_end(GTK_BOX(window_vbox_), drag_helper_->widget(),
594 FALSE, FALSE, 0);
595 }
596
597 gboolean PanelGtk::OnTitlebarButtonPressEvent(
598 GtkWidget* widget, GdkEventButton* event) {
599 if (event->button != 1)
600 return TRUE;
601 if (event->type != GDK_BUTTON_PRESS)
602 return TRUE;
603
604 gdk_window_raise(gtk_widget_get_window(GTK_WIDGET(window_)));
605 EnsureDragHelperCreated();
606 drag_helper_->InitialTitlebarMousePress(event, titlebar_->widget());
607 return TRUE;
608 }
609
610 gboolean PanelGtk::OnTitlebarButtonReleaseEvent(
611 GtkWidget* widget, GdkEventButton* event) {
612 if (event->button != 1)
613 return TRUE;
614
615 panel_->OnTitlebarClicked((event->state & GDK_CONTROL_MASK) ?
616 panel::APPLY_TO_ALL : panel::NO_MODIFIER);
617 return TRUE;
618 }
619
620 gboolean PanelGtk::OnMouseMoveEvent(GtkWidget* widget,
621 GdkEventMotion* event) {
622 // This method is used to update the mouse cursor when over the edge of the
623 // custom frame. If we're over some other widget, do nothing.
624 if (event->window != gtk_widget_get_window(widget)) {
625 // Reset the cursor.
626 if (frame_cursor_) {
627 frame_cursor_ = NULL;
628 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
629 }
630 return FALSE;
631 }
632
633 // Update the cursor if we're on the custom frame border.
634 GdkWindowEdge edge;
635 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x),
636 static_cast<int>(event->y), &edge);
637 GdkCursorType new_cursor = has_hit_edge ?
638 gtk_window_util::GdkWindowEdgeToGdkCursorType(edge) : GDK_LAST_CURSOR;
639 GdkCursorType last_cursor =
640 frame_cursor_ ? frame_cursor_->type : GDK_LAST_CURSOR;
641
642 if (last_cursor != new_cursor) {
643 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
644 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
645 frame_cursor_);
646 }
647 return FALSE;
648 }
649
650 gboolean PanelGtk::OnButtonPressEvent(GtkWidget* widget,
651 GdkEventButton* event) {
652 if (event->button != 1 || event->type != GDK_BUTTON_PRESS)
653 return FALSE;
654
655 // No way to deactivate a window in GTK, so we pretended it is deactivated.
656 // See comments in DeactivatePanel().
657 // Mouse click anywhere in window should re-activate window so do it now.
658 if (!is_active_)
659 panel_->Activate();
660
661 // Make the button press coordinate relative to the panel window.
662 int win_x, win_y;
663 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
664 gdk_window_get_origin(gdk_window, &win_x, &win_y);
665
666 GdkWindowEdge edge;
667 gfx::Point point(static_cast<int>(event->x_root - win_x),
668 static_cast<int>(event->y_root - win_y));
669 bool has_hit_edge = GetWindowEdge(point.x(), point.y(), &edge);
670 if (has_hit_edge) {
671 gdk_window_raise(gdk_window);
672 EnsureDragHelperCreated();
673 // Resize cursor was set by PanelGtk when mouse moved over window edge.
674 GdkCursor* cursor =
675 gdk_window_get_cursor(gtk_widget_get_window(GTK_WIDGET(window_)));
676 drag_helper_->InitialWindowEdgeMousePress(event, cursor, edge);
677 return TRUE;
678 }
679
680 return FALSE; // Continue to propagate the event.
681 }
682
683 void PanelGtk::ActiveWindowChanged(GdkWindow* active_window) {
684 // Do nothing if we're in the process of closing the browser window.
685 if (!window_)
686 return;
687
688 bool is_active = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window;
689 if (is_active == is_active_)
690 return; // State did not change.
691
692 if (is_active) {
693 // If there's an app modal dialog (e.g., JS alert), try to redirect
694 // the user's attention to the window owning the dialog.
695 if (AppModalDialogQueue::GetInstance()->HasActiveDialog()) {
696 AppModalDialogQueue::GetInstance()->ActivateModalDialog();
697 return;
698 }
699 }
700
701 is_active_ = is_active;
702 titlebar_->UpdateTextColor();
703 InvalidateWindow();
704 panel_->OnActiveStateChanged(is_active_);
705 }
706
707 // Callback for the delete event. This event is fired when the user tries to
708 // close the window.
709 gboolean PanelGtk::OnMainWindowDeleteEvent(GtkWidget* widget,
710 GdkEvent* event) {
711 ClosePanel();
712
713 // Return true to prevent the gtk window from being destroyed. Close will
714 // destroy it for us.
715 return TRUE;
716 }
717
718 void PanelGtk::OnMainWindowDestroy(GtkWidget* widget) {
719 // BUG 8712. When we gtk_widget_destroy() in ClosePanel(), this will emit the
720 // signal right away, and we will be here (while ClosePanel() is still in the
721 // call stack). Let stack unwind before deleting the panel.
722 //
723 // We don't want to use DeleteSoon() here since it won't work on a nested pump
724 // (like in UI tests).
725 MessageLoop::current()->PostTask(
726 FROM_HERE, base::Bind(&base::DeletePointer<PanelGtk>, this));
727 }
728
729 void PanelGtk::ShowPanel() {
730 gtk_window_present(window_);
731 RevealPanel();
732 }
733
734 void PanelGtk::ShowPanelInactive() {
735 gtk_window_set_focus_on_map(window_, false);
736 gtk_widget_show(GTK_WIDGET(window_));
737 RevealPanel();
738 }
739
740 void PanelGtk::RevealPanel() {
741 DCHECK(!is_shown_);
742 is_shown_ = true;
743
744 // Grow the window from the botttom up to produce a 'reveal' animation.
745 int top = bounds_.bottom() - configure_size_.height();
746 StartBoundsAnimation(
747 gfx::Rect(bounds_.x(), top, bounds_.width(), configure_size_.height()),
748 bounds_);
749 }
750
751 gfx::Rect PanelGtk::GetPanelBounds() const {
752 return bounds_;
753 }
754
755 void PanelGtk::SetPanelBounds(const gfx::Rect& bounds) {
756 SetBoundsInternal(bounds, true);
757 }
758
759 void PanelGtk::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
760 SetBoundsInternal(bounds, false);
761 }
762
763 void PanelGtk::SetBoundsInternal(const gfx::Rect& bounds, bool animate) {
764 if (bounds == bounds_)
765 return;
766
767 if (!animate) {
768 // If no animation is in progress, apply bounds change instantly. Otherwise,
769 // continue the animation with new target bounds.
770 if (!IsAnimatingBounds())
771 gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
772 bounds.x(), bounds.y(),
773 bounds.width(), bounds.height());
774 } else if (is_shown_) {
775 StartBoundsAnimation(bounds_, bounds);
776 }
777
778 bounds_ = bounds;
779 }
780
781 void PanelGtk::ClosePanel() {
782 // We're already closing. Do nothing.
783 if (!window_)
784 return;
785
786 if (!panel_->ShouldCloseWindow())
787 return;
788
789 if (bounds_animator_.get())
790 bounds_animator_.reset();
791
792 if (drag_helper_.get())
793 drag_helper_.reset();
794
795 if (accel_group_)
796 DisconnectAccelerators();
797
798 // Cancel any pending callback from the loading animation timer.
799 loading_animation_timer_.Stop();
800
801 if (panel_->GetWebContents()) {
802 // Hide the window (so it appears to have closed immediately).
803 // When web contents are destroyed, we will be called back again.
804 gtk_widget_hide(GTK_WIDGET(window_));
805 panel_->OnWindowClosing();
806 return;
807 }
808
809 GtkWidget* window = GTK_WIDGET(window_);
810 // To help catch bugs in any event handlers that might get fired during the
811 // destruction, set window_ to NULL before any handlers will run.
812 window_ = NULL;
813
814 panel_->OnNativePanelClosed();
815
816 // We don't want GlobalMenuBar handling any notifications or commands after
817 // the window is destroyed.
818 // TODO(jennb): global_menu_bar_->Disable();
819 gtk_widget_destroy(window);
820 }
821
822 void PanelGtk::ActivatePanel() {
823 gtk_window_present(window_);
824 }
825
826 void PanelGtk::DeactivatePanel() {
827 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
828
829 // Per ICCCM: http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7
830 // A convention is also required for clients that want to give up the
831 // input focus. There is no safe value set for them to set the input
832 // focus to; therefore, they should ignore input material.
833 //
834 // No way to deactive a GTK window. Pretend panel is deactivated
835 // and ignore input.
836 ActiveWindowChanged(NULL);
837 }
838
839 bool PanelGtk::IsPanelActive() const {
840 return is_active_;
841 }
842
843 void PanelGtk::PreventActivationByOS(bool prevent_activation) {
844 gtk_window_set_accept_focus(window_, !prevent_activation);
845 }
846
847 gfx::NativeWindow PanelGtk::GetNativePanelHandle() {
848 return window_;
849 }
850
851 void PanelGtk::UpdatePanelTitleBar() {
852 TRACE_EVENT0("ui::gtk", "PanelGtk::UpdatePanelTitleBar");
853 string16 title = panel_->GetWindowTitle();
854 gtk_window_set_title(window_, UTF16ToUTF8(title).c_str());
855 titlebar_->UpdateTitleAndIcon();
856 }
857
858 void PanelGtk::UpdatePanelLoadingAnimations(bool should_animate) {
859 if (should_animate) {
860 if (!loading_animation_timer_.IsRunning()) {
861 // Loads are happening, and the timer isn't running, so start it.
862 loading_animation_timer_.Start(FROM_HERE,
863 base::TimeDelta::FromMilliseconds(kLoadingAnimationFrameTimeMs),
864 this,
865 &PanelGtk::LoadingAnimationCallback);
866 }
867 } else {
868 if (loading_animation_timer_.IsRunning()) {
869 loading_animation_timer_.Stop();
870 // Loads are now complete, update the state if a task was scheduled.
871 LoadingAnimationCallback();
872 }
873 }
874 }
875
876 void PanelGtk::LoadingAnimationCallback() {
877 titlebar_->UpdateThrobber(panel_->GetWebContents());
878 }
879
880 FindBar* PanelGtk::CreatePanelFindBar() {
881 return NULL; // legacy
882 }
883
884 void PanelGtk::NotifyPanelOnUserChangedTheme() {
885 titlebar_->UpdateTextColor();
886 InvalidateWindow();
887 }
888
889 void PanelGtk::PanelCut() {
890 gtk_window_util::DoCut(window_, panel_->GetWebContents());
891 }
892
893 void PanelGtk::PanelCopy() {
894 gtk_window_util::DoCopy(window_, panel_->GetWebContents());
895 }
896
897 void PanelGtk::PanelPaste() {
898 gtk_window_util::DoPaste(window_, panel_->GetWebContents());
899 }
900
901 void PanelGtk::DrawAttention(bool draw_attention) {
902 DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0);
903
904 if (is_drawing_attention_ == draw_attention)
905 return;
906
907 is_drawing_attention_ = draw_attention;
908
909 titlebar_->UpdateTextColor();
910 InvalidateWindow();
911
912 if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) {
913 // May not be respected by all window managers.
914 gtk_window_set_urgency_hint(window_, draw_attention);
915 }
916 }
917
918 bool PanelGtk::IsDrawingAttention() const {
919 return is_drawing_attention_;
920 }
921
922 bool PanelGtk::PreHandlePanelKeyboardEvent(
923 const NativeWebKeyboardEvent& event,
924 bool* is_keyboard_shortcut) {
925 // No need to prehandle as no keys are reserved.
926 return false;
927 }
928
929 void PanelGtk::HandlePanelKeyboardEvent(
930 const NativeWebKeyboardEvent& event) {
931 GdkEventKey* os_event = &event.os_event->key;
932 if (os_event && event.type == WebKit::WebInputEvent::RawKeyDown)
933 gtk_window_activate_key(window_, os_event);
934 }
935
936 void PanelGtk::FullScreenModeChanged(bool is_full_screen) {
937 // Nothing to do here as z-order rules for panels ensures that they're below
938 // any app running in full screen mode.
939 }
940
941 void PanelGtk::PanelExpansionStateChanging(
942 Panel::ExpansionState old_state, Panel::ExpansionState new_state) {
943 }
944
945 void PanelGtk::AttachWebContents(content::WebContents* contents) {
946 if (!contents)
947 return;
948 gfx::NativeView widget = contents->GetNativeView();
949 if (widget) {
950 gtk_container_add(GTK_CONTAINER(contents_expanded_), widget);
951 gtk_widget_show(widget);
952 contents->WasShown();
953 }
954 }
955
956 void PanelGtk::DetachWebContents(content::WebContents* contents) {
957 gfx::NativeView widget = contents->GetNativeView();
958 if (widget) {
959 GtkWidget* parent = gtk_widget_get_parent(widget);
960 if (parent) {
961 DCHECK_EQ(parent, contents_expanded_);
962 gtk_container_remove(GTK_CONTAINER(contents_expanded_), widget);
963 }
964 }
965 }
966
967 Browser* PanelGtk::GetPanelBrowser() const {
968 return NULL; // legacy
969 }
970
971 gfx::Size PanelGtk::WindowSizeFromContentSize(
972 const gfx::Size& content_size) const {
973 gfx::Size& frame_size = GetFrameSize();
974 return gfx::Size(content_size.width() + frame_size.width(),
975 content_size.height() + frame_size.height());
976 }
977
978 gfx::Size PanelGtk::ContentSizeFromWindowSize(
979 const gfx::Size& window_size) const {
980 gfx::Size& frame_size = GetFrameSize();
981 return gfx::Size(window_size.width() - frame_size.width(),
982 window_size.height() - frame_size.height());
983 }
984
985 int PanelGtk::TitleOnlyHeight() const {
986 GtkAllocation allocation;
987 gtk_widget_get_allocation(titlebar_->widget(), &allocation);
988 return allocation.height;
989 }
990
991 void PanelGtk::EnsurePanelFullyVisible() {
992 gtk_window_present(window_);
993 }
994
995 void PanelGtk::SetPanelAlwaysOnTop(bool on_top) {
996 gtk_window_set_keep_above(window_, on_top);
997 }
998
999 void PanelGtk::EnableResizeByMouse(bool enable) {
1000 }
1001
1002 void PanelGtk::UpdatePanelMinimizeRestoreButtonVisibility() {
1003 titlebar_->UpdateMinimizeRestoreButtonVisibility();
1004 }
1005
1006 void PanelGtk::StartBoundsAnimation(
1007 const gfx::Rect& from_bounds, const gfx::Rect& to_bounds) {
1008 animation_start_bounds_ = IsAnimatingBounds() ?
1009 last_animation_progressed_bounds_ : from_bounds;
1010
1011 bounds_animator_.reset(new PanelBoundsAnimation(
1012 this, panel_.get(), animation_start_bounds_, to_bounds));
1013
1014 bounds_animator_->Start();
1015 last_animation_progressed_bounds_ = animation_start_bounds_;
1016 }
1017
1018 bool PanelGtk::IsAnimatingBounds() const {
1019 return bounds_animator_.get() && bounds_animator_->is_animating();
1020 }
1021
1022 void PanelGtk::AnimationEnded(const ui::Animation* animation) {
1023 titlebar_->SendEnterNotifyToCloseButtonIfUnderMouse();
1024 panel_->manager()->OnPanelAnimationEnded(panel_.get());
1025 }
1026
1027 void PanelGtk::AnimationProgressed(const ui::Animation* animation) {
1028 DCHECK(is_shown_);
1029 gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween(
1030 animation_start_bounds_, bounds_);
1031
1032 gdk_window_move_resize(gtk_widget_get_window(GTK_WIDGET(window_)),
1033 new_bounds.x(), new_bounds.y(),
1034 new_bounds.width(), new_bounds.height());
1035
1036 last_animation_progressed_bounds_ = new_bounds;
1037 }
1038
1039 gfx::Size PanelGtk::GetNonClientFrameSize() const {
1040 GtkAllocation window_allocation;
1041 gtk_widget_get_allocation(window_container_, &window_allocation);
1042 GtkAllocation contents_allocation;
1043 gtk_widget_get_allocation(contents_expanded_, &contents_allocation);
1044 return gfx::Size(window_allocation.width - contents_allocation.width,
1045 window_allocation.height - contents_allocation.height);
1046 }
1047
1048 void PanelGtk::InvalidateWindow() {
1049 GtkAllocation allocation;
1050 gtk_widget_get_allocation(GTK_WIDGET(window_), &allocation);
1051 gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(window_)),
1052 &allocation, TRUE);
1053 }
1054
1055 // NativePanelTesting implementation.
1056 class GtkNativePanelTesting : public NativePanelTesting {
1057 public:
1058 explicit GtkNativePanelTesting(PanelGtk* panel_gtk);
1059
1060 private:
1061 virtual void PressLeftMouseButtonTitlebar(
1062 const gfx::Point& mouse_location, panel::ClickModifier modifier) OVERRIDE;
1063 virtual void ReleaseMouseButtonTitlebar(
1064 panel::ClickModifier modifier) OVERRIDE;
1065 virtual void DragTitlebar(const gfx::Point& mouse_location) OVERRIDE;
1066 virtual void CancelDragTitlebar() OVERRIDE;
1067 virtual void FinishDragTitlebar() OVERRIDE;
1068 virtual bool VerifyDrawingAttention() const OVERRIDE;
1069 virtual bool VerifyActiveState(bool is_active) OVERRIDE;
1070 virtual void WaitForWindowCreationToComplete() const OVERRIDE;
1071 virtual bool IsWindowSizeKnown() const OVERRIDE;
1072 virtual bool IsAnimatingBounds() const OVERRIDE;
1073 virtual bool IsButtonVisible(
1074 panel::TitlebarButtonType button_type) const OVERRIDE;
1075
1076 PanelGtk* panel_gtk_;
1077 };
1078
1079 NativePanelTesting* PanelGtk::CreateNativePanelTesting() {
1080 return new GtkNativePanelTesting(this);
1081 }
1082
1083 GtkNativePanelTesting::GtkNativePanelTesting(PanelGtk* panel_gtk)
1084 : panel_gtk_(panel_gtk) {
1085 }
1086
1087 void GtkNativePanelTesting::PressLeftMouseButtonTitlebar(
1088 const gfx::Point& mouse_location, panel::ClickModifier modifier) {
1089 // If there is an animation, wait for it to finish as we don't handle button
1090 // clicks while animation is in progress.
1091 while (panel_gtk_->IsAnimatingBounds())
1092 MessageLoopForUI::current()->RunAllPending();
1093
1094 GdkEvent* event = gdk_event_new(GDK_BUTTON_PRESS);
1095 event->button.button = 1;
1096 event->button.x_root = mouse_location.x();
1097 event->button.y_root = mouse_location.y();
1098 if (modifier == panel::APPLY_TO_ALL)
1099 event->button.state |= GDK_CONTROL_MASK;
1100 panel_gtk_->OnTitlebarButtonPressEvent(
1101 NULL, reinterpret_cast<GdkEventButton*>(event));
1102 gdk_event_free(event);
1103 MessageLoopForUI::current()->RunAllPending();
1104 }
1105
1106 void GtkNativePanelTesting::ReleaseMouseButtonTitlebar(
1107 panel::ClickModifier modifier) {
1108 GdkEvent* event = gdk_event_new(GDK_BUTTON_RELEASE);
1109 event->button.button = 1;
1110 if (modifier == panel::APPLY_TO_ALL)
1111 event->button.state |= GDK_CONTROL_MASK;
1112 if (panel_gtk_->drag_helper_.get()) {
1113 panel_gtk_->drag_helper_->OnButtonReleaseEvent(
1114 NULL, reinterpret_cast<GdkEventButton*>(event));
1115 } else {
1116 panel_gtk_->OnTitlebarButtonReleaseEvent(
1117 NULL, reinterpret_cast<GdkEventButton*>(event));
1118 }
1119 gdk_event_free(event);
1120 MessageLoopForUI::current()->RunAllPending();
1121 }
1122
1123 void GtkNativePanelTesting::DragTitlebar(const gfx::Point& mouse_location) {
1124 if (!panel_gtk_->drag_helper_.get())
1125 return;
1126 GdkEvent* event = gdk_event_new(GDK_MOTION_NOTIFY);
1127 event->motion.x_root = mouse_location.x();
1128 event->motion.y_root = mouse_location.y();
1129 panel_gtk_->drag_helper_->OnMouseMoveEvent(
1130 NULL, reinterpret_cast<GdkEventMotion*>(event));
1131 gdk_event_free(event);
1132 MessageLoopForUI::current()->RunAllPending();
1133 }
1134
1135 void GtkNativePanelTesting::CancelDragTitlebar() {
1136 if (!panel_gtk_->drag_helper_.get())
1137 return;
1138 panel_gtk_->drag_helper_->OnGrabBrokenEvent(NULL, NULL);
1139 MessageLoopForUI::current()->RunAllPending();
1140 }
1141
1142 void GtkNativePanelTesting::FinishDragTitlebar() {
1143 if (!panel_gtk_->drag_helper_.get())
1144 return;
1145 ReleaseMouseButtonTitlebar(panel::NO_MODIFIER);
1146 }
1147
1148 bool GtkNativePanelTesting::VerifyDrawingAttention() const {
1149 return panel_gtk_->IsDrawingAttention();
1150 }
1151
1152 bool GtkNativePanelTesting::VerifyActiveState(bool is_active) {
1153 // TODO(jianli): to be implemented. http://crbug.com/102737
1154 return false;
1155 }
1156
1157 void GtkNativePanelTesting::WaitForWindowCreationToComplete() const {
1158 while (GetFrameSize().IsEmpty())
1159 MessageLoopForUI::current()->RunAllPending();
1160 while (panel_gtk_->IsAnimatingBounds())
1161 MessageLoopForUI::current()->RunAllPending();
1162 }
1163
1164 bool GtkNativePanelTesting::IsWindowSizeKnown() const {
1165 return !GetFrameSize().IsEmpty();
1166 }
1167
1168 bool GtkNativePanelTesting::IsAnimatingBounds() const {
1169 return panel_gtk_->IsAnimatingBounds();
1170 }
1171
1172 bool GtkNativePanelTesting::IsButtonVisible(
1173 panel::TitlebarButtonType button_type) const {
1174 PanelTitlebarGtk* titlebar = panel_gtk_->titlebar();
1175 CustomDrawButton* button;
1176 switch (button_type) {
1177 case panel::CLOSE_BUTTON:
1178 button = titlebar->close_button();
1179 break;
1180 case panel::MINIMIZE_BUTTON:
1181 button = titlebar->minimize_button();
1182 break;
1183 case panel::RESTORE_BUTTON:
1184 button = titlebar->restore_button();
1185 break;
1186 default:
1187 NOTREACHED();
1188 return false;
1189 }
1190 return gtk_widget_get_visible(button->widget());
1191 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/panels/panel_gtk.h ('k') | chrome/browser/ui/panels/panel_host.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698