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

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

Powered by Google App Engine
This is Rietveld 408576698