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

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