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

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

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