OLD | NEW |
---|---|
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 } | |
OLD | NEW |