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