OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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/renderer_host/gtk_key_bindings_handler.h" |
| 6 |
| 7 #include <gdk/gdkkeysyms.h> |
| 8 |
| 9 #include <string> |
| 10 |
| 11 #include "base/logging.h" |
| 12 #include "base/string_util.h" |
| 13 #include "chrome/common/native_web_keyboard_event.h" |
| 14 |
| 15 GtkKeyBindingsHandler::GtkKeyBindingsHandler(GtkWidget* parent_widget) |
| 16 : handler_(CreateNewHandler()), |
| 17 edit_commands_(NULL), |
| 18 enabled_(false) { |
| 19 DCHECK(GTK_IS_FIXED(parent_widget)); |
| 20 // We need add the |handler_| object into gtk widget hierarchy, so that |
| 21 // gtk_bindings_activate_event() can find correct display and keymaps from |
| 22 // the |handler_| object. |
| 23 gtk_fixed_put(GTK_FIXED(parent_widget), handler_.get(), -1, -1); |
| 24 } |
| 25 |
| 26 GtkKeyBindingsHandler::~GtkKeyBindingsHandler() { |
| 27 handler_.Destroy(); |
| 28 } |
| 29 |
| 30 bool GtkKeyBindingsHandler::Match(const NativeWebKeyboardEvent& wke, |
| 31 EditCommands* edit_commands) { |
| 32 if (!enabled_ || wke.type == WebKit::WebInputEvent::Char || |
| 33 !wke.os_event || wke.os_event->keyval == GDK_VoidSymbol) |
| 34 return false; |
| 35 |
| 36 edit_commands_.clear(); |
| 37 // If this key event matches a predefined key binding, corresponding signal |
| 38 // will be emitted. |
| 39 gtk_bindings_activate_event(GTK_OBJECT(handler_.get()), wke.os_event); |
| 40 |
| 41 bool matched = !edit_commands_.empty(); |
| 42 if (edit_commands) |
| 43 edit_commands->swap(edit_commands_); |
| 44 return matched; |
| 45 } |
| 46 |
| 47 GtkWidget* GtkKeyBindingsHandler::CreateNewHandler() { |
| 48 Handler* handler = |
| 49 static_cast<Handler*>(g_object_new(HandlerGetType(), NULL)); |
| 50 |
| 51 handler->owner = this; |
| 52 |
| 53 // We don't need to show the |handler| object on screen, so set its size to |
| 54 // zero. |
| 55 gtk_widget_set_size_request(GTK_WIDGET(handler), 0, 0); |
| 56 |
| 57 // Prevents it from handling any events by itself. |
| 58 gtk_widget_set_sensitive(GTK_WIDGET(handler), FALSE); |
| 59 gtk_widget_set_events(GTK_WIDGET(handler), 0); |
| 60 GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(handler), GTK_CAN_FOCUS); |
| 61 |
| 62 #if !GTK_CHECK_VERSION(2, 14, 0) |
| 63 // "move-viewport", "select-all" and "toggle-cursor-visible" have no |
| 64 // corresponding virtual methods. Prior to glib 2.18 (gtk 2.14), there is no |
| 65 // way to override the default class handler of a signal. So we need hook |
| 66 // these signal explicitly. |
| 67 g_signal_connect(handler, "move-viewport", G_CALLBACK(MoveViewport), NULL); |
| 68 g_signal_connect(handler, "select-all", G_CALLBACK(SelectAll), NULL); |
| 69 g_signal_connect(handler, "toggle-cursor-visible", |
| 70 G_CALLBACK(ToggleCursorVisible), NULL); |
| 71 #endif |
| 72 return GTK_WIDGET(handler); |
| 73 } |
| 74 |
| 75 void GtkKeyBindingsHandler::EditCommandMatched( |
| 76 const std::string& name, const std::string& value) { |
| 77 edit_commands_.push_back(EditCommand(name, value)); |
| 78 } |
| 79 |
| 80 void GtkKeyBindingsHandler::HandlerInit(Handler *self) { |
| 81 self->owner = NULL; |
| 82 } |
| 83 |
| 84 void GtkKeyBindingsHandler::HandlerClassInit(HandlerClass *klass) { |
| 85 GtkTextViewClass* text_view_class = GTK_TEXT_VIEW_CLASS(klass); |
| 86 |
| 87 // Overrides all virtual methods related to editor key bindings. |
| 88 text_view_class->backspace = BackSpace; |
| 89 text_view_class->copy_clipboard = CopyClipboard; |
| 90 text_view_class->cut_clipboard = CutClipboard; |
| 91 text_view_class->delete_from_cursor = DeleteFromCursor; |
| 92 text_view_class->insert_at_cursor = InsertAtCursor; |
| 93 text_view_class->move_cursor = MoveCursor; |
| 94 text_view_class->paste_clipboard = PasteClipboard; |
| 95 text_view_class->set_anchor = SetAnchor; |
| 96 text_view_class->toggle_overwrite = ToggleOverwrite; |
| 97 |
| 98 #if GTK_CHECK_VERSION(2, 14, 0) |
| 99 // "move-viewport", "select-all" and "toggle-cursor-visible" have no |
| 100 // corresponding virtual methods. Since glib 2.18 (gtk 2.14), |
| 101 // g_signal_override_class_handler() is introduced to override a signal |
| 102 // handler. |
| 103 g_signal_override_class_handler("move-viewport", |
| 104 G_TYPE_FROM_CLASS(klass), |
| 105 G_CALLBACK(MoveViewport)); |
| 106 |
| 107 g_signal_override_class_handler("select-all", |
| 108 G_TYPE_FROM_CLASS(klass), |
| 109 G_CALLBACK(SelectAll)); |
| 110 |
| 111 g_signal_override_class_handler("toggle-cursor-visible", |
| 112 G_TYPE_FROM_CLASS(klass), |
| 113 G_CALLBACK(ToggleCursorVisible)); |
| 114 #endif |
| 115 } |
| 116 |
| 117 GType GtkKeyBindingsHandler::HandlerGetType() { |
| 118 static volatile gsize type_id_volatile = 0; |
| 119 if (g_once_init_enter(&type_id_volatile)) { |
| 120 GType type_id = g_type_register_static_simple( |
| 121 GTK_TYPE_TEXT_VIEW, |
| 122 g_intern_static_string("GtkKeyBindingsHandler"), |
| 123 sizeof(HandlerClass), |
| 124 reinterpret_cast<GClassInitFunc>(HandlerClassInit), |
| 125 sizeof(Handler), |
| 126 reinterpret_cast<GInstanceInitFunc>(HandlerInit), |
| 127 static_cast<GTypeFlags>(0)); |
| 128 g_once_init_leave(&type_id_volatile, type_id); |
| 129 } |
| 130 return type_id_volatile; |
| 131 } |
| 132 |
| 133 GtkKeyBindingsHandler* GtkKeyBindingsHandler::GetHandlerOwner( |
| 134 GtkTextView* text_view) { |
| 135 Handler* handler = G_TYPE_CHECK_INSTANCE_CAST( |
| 136 text_view, HandlerGetType(), Handler); |
| 137 DCHECK(handler); |
| 138 return handler->owner; |
| 139 } |
| 140 |
| 141 void GtkKeyBindingsHandler::BackSpace(GtkTextView* text_view) { |
| 142 GetHandlerOwner(text_view)->EditCommandMatched("DeleteBackward", ""); |
| 143 } |
| 144 |
| 145 void GtkKeyBindingsHandler::CopyClipboard(GtkTextView* text_view) { |
| 146 GetHandlerOwner(text_view)->EditCommandMatched("Copy", ""); |
| 147 } |
| 148 |
| 149 void GtkKeyBindingsHandler::CutClipboard(GtkTextView* text_view) { |
| 150 GetHandlerOwner(text_view)->EditCommandMatched("Cut", ""); |
| 151 } |
| 152 |
| 153 void GtkKeyBindingsHandler::DeleteFromCursor( |
| 154 GtkTextView* text_view, GtkDeleteType type, gint count) { |
| 155 if (!count) |
| 156 return; |
| 157 |
| 158 const char *commands[3] = { NULL, NULL, NULL }; |
| 159 switch (type) { |
| 160 case GTK_DELETE_CHARS: |
| 161 commands[0] = (count > 0 ? "DeleteForward" : "DeleteBackward"); |
| 162 break; |
| 163 case GTK_DELETE_WORD_ENDS: |
| 164 commands[0] = (count > 0 ? "DeleteWordForward" : "DeleteWordBackward"); |
| 165 break; |
| 166 case GTK_DELETE_WORDS: |
| 167 if (count > 0) { |
| 168 commands[0] = "MoveWordForward"; |
| 169 commands[1] = "DeleteWordBackward"; |
| 170 } else { |
| 171 commands[0] = "MoveWordBackward"; |
| 172 commands[1] = "DeleteWordForward"; |
| 173 } |
| 174 break; |
| 175 case GTK_DELETE_DISPLAY_LINES: |
| 176 commands[0] = "MoveToBeginningOfLine"; |
| 177 commands[1] = "DeleteToEndOfLine"; |
| 178 break; |
| 179 case GTK_DELETE_DISPLAY_LINE_ENDS: |
| 180 commands[0] = (count > 0 ? "DeleteToEndOfLine" : |
| 181 "DeleteToBeginningOfLine"); |
| 182 break; |
| 183 case GTK_DELETE_PARAGRAPH_ENDS: |
| 184 commands[0] = (count > 0 ? "DeleteToEndOfParagraph" : |
| 185 "DeleteToBeginningOfParagraph"); |
| 186 break; |
| 187 case GTK_DELETE_PARAGRAPHS: |
| 188 commands[0] = "MoveToBeginningOfParagraph"; |
| 189 commands[1] = "DeleteToEndOfParagraph"; |
| 190 break; |
| 191 default: |
| 192 // GTK_DELETE_WHITESPACE has no corresponding editor command. |
| 193 return; |
| 194 } |
| 195 |
| 196 GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view); |
| 197 if (count < 0) |
| 198 count = -count; |
| 199 for (; count > 0; --count) { |
| 200 for (const char* const* p = commands; *p; ++p) |
| 201 owner->EditCommandMatched(*p, ""); |
| 202 } |
| 203 } |
| 204 |
| 205 void GtkKeyBindingsHandler::InsertAtCursor(GtkTextView* text_view, |
| 206 const gchar* str) { |
| 207 if (str && *str) |
| 208 GetHandlerOwner(text_view)->EditCommandMatched("InsertText", str); |
| 209 } |
| 210 |
| 211 void GtkKeyBindingsHandler::MoveCursor( |
| 212 GtkTextView* text_view, GtkMovementStep step, gint count, |
| 213 gboolean extend_selection) { |
| 214 if (!count) |
| 215 return; |
| 216 |
| 217 std::string command; |
| 218 switch (step) { |
| 219 case GTK_MOVEMENT_LOGICAL_POSITIONS: |
| 220 command = (count > 0 ? "MoveForward" : "MoveBackward"); |
| 221 break; |
| 222 case GTK_MOVEMENT_VISUAL_POSITIONS: |
| 223 command = (count > 0 ? "MoveRight" : "MoveLeft"); |
| 224 break; |
| 225 case GTK_MOVEMENT_WORDS: |
| 226 command = (count > 0 ? "MoveWordForward" : "MoveWordBackward"); |
| 227 break; |
| 228 case GTK_MOVEMENT_DISPLAY_LINES: |
| 229 command = (count > 0 ? "MoveDown" : "MoveUp"); |
| 230 break; |
| 231 case GTK_MOVEMENT_DISPLAY_LINE_ENDS: |
| 232 command = (count > 0 ? "MoveToEndOfLine" : "MoveToBeginningOfLine"); |
| 233 break; |
| 234 case GTK_MOVEMENT_PARAGRAPH_ENDS: |
| 235 command = (count > 0 ? "MoveToEndOfParagraph" : |
| 236 "MoveToBeginningOfParagraph"); |
| 237 break; |
| 238 case GTK_MOVEMENT_PAGES: |
| 239 command = (count > 0 ? "MovePageDown" : "MovePageUp"); |
| 240 break; |
| 241 case GTK_MOVEMENT_BUFFER_ENDS: |
| 242 command = (count > 0 ? "MoveToEndOfDocument" : |
| 243 "MoveToBeginningOfDocument"); |
| 244 break; |
| 245 default: |
| 246 // GTK_MOVEMENT_PARAGRAPHS and GTK_MOVEMENT_HORIZONTAL_PAGES have |
| 247 // no corresponding editor commands. |
| 248 return; |
| 249 } |
| 250 |
| 251 GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view); |
| 252 if (extend_selection) |
| 253 command.append("AndModifySelection"); |
| 254 if (count < 0) |
| 255 count = -count; |
| 256 for (; count > 0; --count) |
| 257 owner->EditCommandMatched(command, ""); |
| 258 } |
| 259 |
| 260 void GtkKeyBindingsHandler::MoveViewport( |
| 261 GtkTextView* text_view, GtkScrollStep step, gint count) { |
| 262 // Not supported by webkit. |
| 263 #if !GTK_CHECK_VERSION(2, 14, 0) |
| 264 // Before gtk 2.14.0, there is no way to override a non-virtual default signal |
| 265 // handler, so we need stop the signal emission explicitly to prevent the |
| 266 // default handler from being executed. |
| 267 g_signal_stop_emission_by_name(text_view, "move-viewport"); |
| 268 #endif |
| 269 } |
| 270 |
| 271 void GtkKeyBindingsHandler::PasteClipboard(GtkTextView* text_view) { |
| 272 GetHandlerOwner(text_view)->EditCommandMatched("Paste", ""); |
| 273 } |
| 274 |
| 275 void GtkKeyBindingsHandler::SelectAll(GtkTextView* text_view, gboolean select) { |
| 276 if (select) |
| 277 GetHandlerOwner(text_view)->EditCommandMatched("SelectAll", ""); |
| 278 else |
| 279 GetHandlerOwner(text_view)->EditCommandMatched("Unselect", ""); |
| 280 #if !GTK_CHECK_VERSION(2, 14, 0) |
| 281 // Before gtk 2.14.0, there is no way to override a non-virtual default signal |
| 282 // handler, so we need stop the signal emission explicitly to prevent the |
| 283 // default handler from being executed. |
| 284 g_signal_stop_emission_by_name(text_view, "select-all"); |
| 285 #endif |
| 286 } |
| 287 |
| 288 void GtkKeyBindingsHandler::SetAnchor(GtkTextView* text_view) { |
| 289 GetHandlerOwner(text_view)->EditCommandMatched("SetMark", ""); |
| 290 } |
| 291 |
| 292 void GtkKeyBindingsHandler::ToggleCursorVisible(GtkTextView* text_view) { |
| 293 // Not supported by webkit. |
| 294 #if !GTK_CHECK_VERSION(2, 14, 0) |
| 295 // Before gtk 2.14.0, there is no way to override a non-virtual default signal |
| 296 // handler, so we need stop the signal emission explicitly to prevent the |
| 297 // default handler from being executed. |
| 298 g_signal_stop_emission_by_name(text_view, "toggle-cursor-visible"); |
| 299 #endif |
| 300 } |
| 301 |
| 302 void GtkKeyBindingsHandler::ToggleOverwrite(GtkTextView* text_view) { |
| 303 // Not supported by webkit. |
| 304 } |
OLD | NEW |