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