OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ui/panels/panel_drag_gtk.h" |
| 6 |
| 7 #include <gdk/gdkkeysyms.h> |
| 8 |
| 9 #include "chrome/browser/ui/panels/panel.h" |
| 10 #include "chrome/browser/ui/panels/panel_constants.h" |
| 11 #include "chrome/browser/ui/panels/panel_manager.h" |
| 12 #include "ui/gfx/gtk_util.h" |
| 13 |
| 14 namespace { |
| 15 |
| 16 panel::ResizingSides GdkWindowEdgeToResizingSide(GdkWindowEdge edge) { |
| 17 switch (edge) { |
| 18 case GDK_WINDOW_EDGE_NORTH_WEST: |
| 19 return panel::RESIZE_TOP_LEFT; |
| 20 case GDK_WINDOW_EDGE_NORTH: |
| 21 return panel::RESIZE_TOP; |
| 22 case GDK_WINDOW_EDGE_NORTH_EAST: |
| 23 return panel::RESIZE_TOP_RIGHT; |
| 24 case GDK_WINDOW_EDGE_WEST: |
| 25 return panel::RESIZE_LEFT; |
| 26 case GDK_WINDOW_EDGE_EAST: |
| 27 return panel::RESIZE_RIGHT; |
| 28 case GDK_WINDOW_EDGE_SOUTH_WEST: |
| 29 return panel::RESIZE_BOTTOM_LEFT; |
| 30 case GDK_WINDOW_EDGE_SOUTH: |
| 31 return panel::RESIZE_BOTTOM; |
| 32 case GDK_WINDOW_EDGE_SOUTH_EAST: |
| 33 return panel::RESIZE_BOTTOM_RIGHT; |
| 34 default: |
| 35 return panel::RESIZE_NONE; |
| 36 } |
| 37 } |
| 38 |
| 39 } // namespace |
| 40 |
| 41 // Virtual base class to abstract move vs resize drag logic. |
| 42 class PanelDragDelegate { |
| 43 public: |
| 44 explicit PanelDragDelegate(Panel* panel) : panel_(panel) {} |
| 45 virtual ~PanelDragDelegate() {} |
| 46 |
| 47 Panel* panel() const { return panel_; } |
| 48 PanelManager* panel_manager() const { return panel_->manager(); } |
| 49 |
| 50 // |point| is the mouse location in screen coordinates. |
| 51 virtual void DragStarted(gfx::Point point) = 0; |
| 52 virtual void Dragged(gfx::Point point) = 0; |
| 53 |
| 54 // |canceled| is true to abort the drag. |
| 55 virtual void DragEnded(bool canceled) = 0; |
| 56 |
| 57 private: |
| 58 Panel* panel_; // Weak pointer to the panel being dragged. |
| 59 |
| 60 DISALLOW_COPY_AND_ASSIGN(PanelDragDelegate); |
| 61 }; |
| 62 |
| 63 // Delegate for moving a panel by dragging the mouse. |
| 64 class MoveDragDelegate : public PanelDragDelegate { |
| 65 public: |
| 66 explicit MoveDragDelegate(Panel* panel) |
| 67 : PanelDragDelegate(panel) {} |
| 68 ~MoveDragDelegate() {} |
| 69 |
| 70 void DragStarted(gfx::Point point) OVERRIDE { |
| 71 panel_manager()->StartDragging(panel(), point); |
| 72 } |
| 73 void Dragged(gfx::Point point) OVERRIDE { |
| 74 panel_manager()->Drag(point); |
| 75 } |
| 76 void DragEnded(bool canceled) OVERRIDE { |
| 77 panel_manager()->EndDragging(canceled); |
| 78 } |
| 79 |
| 80 DISALLOW_COPY_AND_ASSIGN(MoveDragDelegate); |
| 81 }; |
| 82 |
| 83 // Delegate for resizing a panel by dragging the mouse. |
| 84 class ResizeDragDelegate : public PanelDragDelegate { |
| 85 public: |
| 86 ResizeDragDelegate(Panel* panel, GdkWindowEdge edge) |
| 87 : PanelDragDelegate(panel), |
| 88 resizing_side_(GdkWindowEdgeToResizingSide(edge)) {} |
| 89 ~ResizeDragDelegate() {} |
| 90 |
| 91 void DragStarted(gfx::Point point) OVERRIDE { |
| 92 panel_manager()->StartResizingByMouse(panel(), point, resizing_side_); |
| 93 } |
| 94 void Dragged(gfx::Point point) OVERRIDE { |
| 95 panel_manager()->ResizeByMouse(point); |
| 96 } |
| 97 void DragEnded(bool canceled) OVERRIDE { |
| 98 panel_manager()->EndResizingByMouse(canceled); |
| 99 } |
| 100 private: |
| 101 // The edge from which the panel is being resized. |
| 102 panel::ResizingSides resizing_side_; |
| 103 |
| 104 DISALLOW_COPY_AND_ASSIGN(ResizeDragDelegate); |
| 105 }; |
| 106 |
| 107 // Panel drag helper for processing mouse and keyboard events while |
| 108 // the left mouse button is pressed. |
| 109 PanelDragGtk::PanelDragGtk(Panel* panel) |
| 110 : panel_(panel), |
| 111 drag_state_(NOT_DRAGGING), |
| 112 initial_mouse_down_(NULL), |
| 113 click_handler_(NULL), |
| 114 drag_delegate_(NULL) { |
| 115 // Create an invisible event box to receive mouse and key events. |
| 116 drag_widget_ = gtk_event_box_new(); |
| 117 gtk_event_box_set_visible_window(GTK_EVENT_BOX(drag_widget_), FALSE); |
| 118 |
| 119 // Connect signals for events during a drag. |
| 120 g_signal_connect(drag_widget_, "motion-notify-event", |
| 121 G_CALLBACK(OnMouseMoveEventThunk), this); |
| 122 g_signal_connect(drag_widget_, "key-press-event", |
| 123 G_CALLBACK(OnKeyPressEventThunk), this); |
| 124 g_signal_connect(drag_widget_, "key-release-event", |
| 125 G_CALLBACK(OnKeyReleaseEventThunk), this); |
| 126 g_signal_connect(drag_widget_, "button-press-event", |
| 127 G_CALLBACK(OnButtonPressEventThunk), this); |
| 128 g_signal_connect(drag_widget_, "button-release-event", |
| 129 G_CALLBACK(OnButtonReleaseEventThunk), this); |
| 130 g_signal_connect(drag_widget_, "grab-broken-event", |
| 131 G_CALLBACK(OnGrabBrokenEventThunk), this); |
| 132 } |
| 133 |
| 134 PanelDragGtk::~PanelDragGtk() { |
| 135 EndDrag(true); // Clean up drag state. |
| 136 } |
| 137 |
| 138 void PanelDragGtk::AssertCleanState() { |
| 139 DCHECK_EQ(NOT_DRAGGING, drag_state_); |
| 140 DCHECK(!drag_delegate_); |
| 141 DCHECK(!initial_mouse_down_); |
| 142 DCHECK(!click_handler_); |
| 143 } |
| 144 |
| 145 void PanelDragGtk::InitialWindowEdgeMousePress(GdkEventButton* event, |
| 146 GdkCursor* cursor, |
| 147 GdkWindowEdge& edge) { |
| 148 AssertCleanState(); |
| 149 drag_delegate_ = new ResizeDragDelegate(panel_, edge); |
| 150 GrabPointerAndKeyboard(event, cursor); |
| 151 } |
| 152 |
| 153 void PanelDragGtk::InitialTitlebarMousePress(GdkEventButton* event, |
| 154 GtkWidget* titlebar_widget) { |
| 155 AssertCleanState(); |
| 156 click_handler_ = titlebar_widget; |
| 157 drag_delegate_ = new MoveDragDelegate(panel_); |
| 158 GrabPointerAndKeyboard(event, gfx::GetCursor(GDK_FLEUR)); // Drag cursor. |
| 159 } |
| 160 |
| 161 void PanelDragGtk::GrabPointerAndKeyboard(GdkEventButton* event, |
| 162 GdkCursor* cursor) { |
| 163 // Remember initial mouse event for use in determining when drag |
| 164 // threshold has been exceeded. |
| 165 initial_mouse_down_ = gdk_event_copy(reinterpret_cast<GdkEvent*>(event)); |
| 166 |
| 167 // Grab pointer and keyboard to make sure we have the focus and get |
| 168 // all mouse and keyboard events during the drag. |
| 169 GdkWindow* gdk_window = gtk_widget_get_window(drag_widget_); |
| 170 DCHECK(gdk_window); |
| 171 GdkGrabStatus pointer_grab_status = |
| 172 gdk_pointer_grab(gdk_window, |
| 173 TRUE, |
| 174 GdkEventMask(GDK_BUTTON_PRESS_MASK | |
| 175 GDK_BUTTON_RELEASE_MASK | |
| 176 GDK_POINTER_MOTION_MASK), |
| 177 NULL, |
| 178 cursor, |
| 179 event->time); |
| 180 GdkGrabStatus keyboard_grab_status = |
| 181 gdk_keyboard_grab(gdk_window, TRUE, event->time); |
| 182 if (pointer_grab_status != GDK_GRAB_SUCCESS || |
| 183 keyboard_grab_status != GDK_GRAB_SUCCESS) { |
| 184 // Grab could fail if someone else already has the pointer/keyboard |
| 185 // grabbed. Cancel the drag. |
| 186 DLOG(ERROR) << "Unable to grab pointer or keyboard (pointer_status=" |
| 187 << pointer_grab_status << ", keyboard_status=" |
| 188 << keyboard_grab_status << ")"; |
| 189 EndDrag(true); |
| 190 return; |
| 191 } |
| 192 |
| 193 gtk_grab_add(drag_widget_); |
| 194 } |
| 195 |
| 196 void PanelDragGtk::EndDrag(bool canceled) { |
| 197 if (initial_mouse_down_) { |
| 198 gdk_event_free(initial_mouse_down_); |
| 199 initial_mouse_down_ = NULL; |
| 200 } |
| 201 |
| 202 if (drag_delegate_) { |
| 203 gdk_pointer_ungrab(GDK_CURRENT_TIME); |
| 204 gdk_keyboard_ungrab(GDK_CURRENT_TIME); |
| 205 gtk_grab_remove(drag_widget_); |
| 206 |
| 207 if (drag_state_ == DRAG_IN_PROGRESS) { |
| 208 drag_delegate_->DragEnded(canceled); |
| 209 drag_state_ = NOT_DRAGGING; |
| 210 } |
| 211 |
| 212 delete drag_delegate_; |
| 213 drag_delegate_ = NULL; |
| 214 } |
| 215 |
| 216 click_handler_ = NULL; |
| 217 } |
| 218 |
| 219 gboolean PanelDragGtk::OnMouseMoveEvent(GtkWidget* widget, |
| 220 GdkEventMotion* event) { |
| 221 DCHECK(drag_delegate_); |
| 222 |
| 223 gdouble new_x_double; |
| 224 gdouble new_y_double; |
| 225 gdk_event_get_root_coords(reinterpret_cast<GdkEvent*>(event), |
| 226 &new_x_double, &new_y_double); |
| 227 gint new_x = static_cast<gint>(new_x_double); |
| 228 gint new_y = static_cast<gint>(new_y_double); |
| 229 |
| 230 // Begin dragging only after mouse has moved beyond the drag threshold. |
| 231 if (drag_state_ == NOT_DRAGGING) { |
| 232 DCHECK(initial_mouse_down_); |
| 233 gdouble old_x_double; |
| 234 gdouble old_y_double; |
| 235 gdk_event_get_root_coords(initial_mouse_down_, |
| 236 &old_x_double, &old_y_double); |
| 237 gint old_x = static_cast<gint>(old_x_double); |
| 238 gint old_y = static_cast<gint>(old_y_double); |
| 239 |
| 240 if (gtk_drag_check_threshold(drag_widget_, old_x, old_y, |
| 241 new_x, new_y)) { |
| 242 drag_state_ = DRAG_IN_PROGRESS; |
| 243 drag_delegate_->DragStarted(gfx::Point(old_x, old_y)); |
| 244 gdk_event_free(initial_mouse_down_); |
| 245 initial_mouse_down_ = NULL; |
| 246 } |
| 247 } |
| 248 |
| 249 if (drag_state_ == DRAG_IN_PROGRESS) |
| 250 drag_delegate_->Dragged(gfx::Point(new_x, new_y)); |
| 251 |
| 252 return TRUE; |
| 253 } |
| 254 |
| 255 gboolean PanelDragGtk::OnButtonPressEvent(GtkWidget* widget, |
| 256 GdkEventButton* event) { |
| 257 DCHECK(drag_delegate_); |
| 258 return TRUE; |
| 259 } |
| 260 |
| 261 gboolean PanelDragGtk::OnButtonReleaseEvent(GtkWidget* widget, |
| 262 GdkEventButton* event) { |
| 263 DCHECK(drag_delegate_); |
| 264 |
| 265 if (event->button == 1) { |
| 266 // Treat release as a mouse click if drag was never started. |
| 267 if (drag_state_ == NOT_DRAGGING && click_handler_) { |
| 268 gtk_propagate_event(click_handler_, |
| 269 reinterpret_cast<GdkEvent*>(event)); |
| 270 } |
| 271 // Cleanup state regardless. |
| 272 EndDrag(false); |
| 273 } |
| 274 |
| 275 return TRUE; |
| 276 } |
| 277 |
| 278 gboolean PanelDragGtk::OnKeyPressEvent(GtkWidget* widget, |
| 279 GdkEventKey* event) { |
| 280 DCHECK(drag_delegate_); |
| 281 return TRUE; |
| 282 } |
| 283 |
| 284 gboolean PanelDragGtk::OnKeyReleaseEvent(GtkWidget* widget, |
| 285 GdkEventKey* event) { |
| 286 DCHECK(drag_delegate_); |
| 287 |
| 288 switch (event->keyval) { |
| 289 case GDK_Escape: |
| 290 EndDrag(true); // Cancel drag. |
| 291 break; |
| 292 case GDK_Return: |
| 293 case GDK_KP_Enter: |
| 294 case GDK_ISO_Enter: |
| 295 case GDK_space: |
| 296 EndDrag(false); // Normal end. |
| 297 break; |
| 298 } |
| 299 return TRUE; |
| 300 } |
| 301 |
| 302 gboolean PanelDragGtk::OnGrabBrokenEvent(GtkWidget* widget, |
| 303 GdkEventGrabBroken* event) { |
| 304 DCHECK(drag_delegate_); |
| 305 EndDrag(true); // Cancel drag. |
| 306 return TRUE; |
| 307 } |
OLD | NEW |