gtk_key_bindings_handler.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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. #include "ui/gtk/gtk_key_bindings_handler.h"
  5. #include <cstddef>
  6. #include <string>
  7. #include "base/logging.h"
  8. #include "base/strings/string_util.h"
  9. #include "ui/base/glib/glib_cast.h"
  10. #include "ui/base/ime/text_edit_commands.h"
  11. #include "ui/events/event.h"
  12. #include "ui/gtk/gtk_compat.h"
  13. #include "ui/gtk/gtk_util.h"
  14. using ui::TextEditCommand;
  15. namespace gtk {
  16. GtkKeyBindingsHandler::GtkKeyBindingsHandler()
  17. : fake_window_(gtk_offscreen_window_new()), handler_(CreateNewHandler()) {
  18. DCHECK(!GtkCheckVersion(4));
  19. gtk_container_add(
  20. GlibCast<GtkContainer>(fake_window_, gtk_container_get_type()), handler_);
  21. }
  22. GtkKeyBindingsHandler::~GtkKeyBindingsHandler() {
  23. gtk_widget_destroy(handler_);
  24. gtk_widget_destroy(fake_window_);
  25. }
  26. bool GtkKeyBindingsHandler::MatchEvent(
  27. const ui::Event& event,
  28. std::vector<ui::TextEditCommandAuraLinux>* edit_commands) {
  29. CHECK(event.IsKeyEvent());
  30. const ui::KeyEvent& key_event = static_cast<const ui::KeyEvent&>(event);
  31. if (key_event.is_char())
  32. return false;
  33. GdkEvent* gdk_event = GdkEventFromKeyEvent(key_event);
  34. if (!gdk_event)
  35. return false;
  36. edit_commands_.clear();
  37. // If this key event matches a predefined key binding, corresponding signal
  38. // will be emitted.
  39. auto* key = reinterpret_cast<GdkEventKey*>(gdk_event);
  40. DCHECK(key->type == GdkKeyPress() || key->type == GdkKeyRelease());
  41. gtk_bindings_activate_event(G_OBJECT(handler_), key);
  42. gdk_event_free(gdk_event);
  43. bool matched = !edit_commands_.empty();
  44. if (edit_commands)
  45. edit_commands->swap(edit_commands_);
  46. return matched;
  47. }
  48. GtkWidget* GtkKeyBindingsHandler::CreateNewHandler() {
  49. Handler* handler =
  50. static_cast<Handler*>(g_object_new(HandlerGetType(), nullptr));
  51. handler->owner = this;
  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. // Prevents it from handling any events by itself.
  56. gtk_widget_set_sensitive(GTK_WIDGET(handler), FALSE);
  57. gtk_widget_set_events(GTK_WIDGET(handler), 0);
  58. gtk_widget_set_can_focus(GTK_WIDGET(handler), TRUE);
  59. return GTK_WIDGET(handler);
  60. }
  61. void GtkKeyBindingsHandler::EditCommandMatched(TextEditCommand command,
  62. const std::string& value) {
  63. edit_commands_.emplace_back(command, value);
  64. }
  65. void GtkKeyBindingsHandler::HandlerInit(Handler* self) {
  66. self->owner = nullptr;
  67. }
  68. void GtkKeyBindingsHandler::HandlerClassInit(HandlerClass* klass) {
  69. // Overrides all virtual methods related to editor key bindings.
  70. klass->backspace = BackSpace;
  71. klass->copy_clipboard = CopyClipboard;
  72. klass->cut_clipboard = CutClipboard;
  73. klass->delete_from_cursor = DeleteFromCursor;
  74. klass->insert_at_cursor = InsertAtCursor;
  75. klass->move_cursor = MoveCursor;
  76. klass->paste_clipboard = PasteClipboard;
  77. klass->set_anchor = SetAnchor;
  78. klass->toggle_overwrite = ToggleOverwrite;
  79. // "move-focus", "move-viewport", "select-all" and "toggle-cursor-visible"
  80. // have no corresponding virtual methods. Since glib 2.18 (gtk 2.14),
  81. // g_signal_override_class_handler() is introduced to override a signal
  82. // handler.
  83. g_signal_override_class_handler("move-focus", G_TYPE_FROM_CLASS(klass),
  84. G_CALLBACK(MoveFocus));
  85. g_signal_override_class_handler("move-viewport", G_TYPE_FROM_CLASS(klass),
  86. G_CALLBACK(MoveViewport));
  87. g_signal_override_class_handler("select-all", G_TYPE_FROM_CLASS(klass),
  88. G_CALLBACK(SelectAll));
  89. g_signal_override_class_handler("toggle-cursor-visible",
  90. G_TYPE_FROM_CLASS(klass),
  91. G_CALLBACK(ToggleCursorVisible));
  92. g_signal_override_class_handler("show-help", G_TYPE_FROM_CLASS(klass),
  93. G_CALLBACK(ShowHelp));
  94. }
  95. GType GtkKeyBindingsHandler::HandlerGetType() {
  96. static volatile gsize type_id_volatile = 0;
  97. if (g_once_init_enter(&type_id_volatile)) {
  98. GType type_id = g_type_register_static_simple(
  99. GTK_TYPE_TEXT_VIEW, g_intern_static_string("GtkKeyBindingsHandler"),
  100. sizeof(HandlerClass),
  101. reinterpret_cast<GClassInitFunc>(HandlerClassInit), sizeof(Handler),
  102. reinterpret_cast<GInstanceInitFunc>(HandlerInit),
  103. static_cast<GTypeFlags>(0));
  104. g_once_init_leave(&type_id_volatile, type_id);
  105. }
  106. return type_id_volatile;
  107. }
  108. GtkKeyBindingsHandler* GtkKeyBindingsHandler::GetHandlerOwner(
  109. GtkTextView* text_view) {
  110. Handler* handler =
  111. G_TYPE_CHECK_INSTANCE_CAST(text_view, HandlerGetType(), Handler);
  112. DCHECK(handler);
  113. return handler->owner;
  114. }
  115. void GtkKeyBindingsHandler::BackSpace(GtkTextView* text_view) {
  116. GetHandlerOwner(text_view)->EditCommandMatched(
  117. TextEditCommand::DELETE_BACKWARD, std::string());
  118. }
  119. void GtkKeyBindingsHandler::CopyClipboard(GtkTextView* text_view) {
  120. GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::COPY,
  121. std::string());
  122. }
  123. void GtkKeyBindingsHandler::CutClipboard(GtkTextView* text_view) {
  124. GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::CUT,
  125. std::string());
  126. }
  127. void GtkKeyBindingsHandler::DeleteFromCursor(GtkTextView* text_view,
  128. GtkDeleteType type,
  129. gint count) {
  130. if (!count)
  131. return;
  132. TextEditCommand commands[2] = {
  133. TextEditCommand::INVALID_COMMAND,
  134. TextEditCommand::INVALID_COMMAND,
  135. };
  136. switch (type) {
  137. case GTK_DELETE_CHARS:
  138. commands[0] = (count > 0 ? TextEditCommand::DELETE_FORWARD
  139. : TextEditCommand::DELETE_BACKWARD);
  140. break;
  141. case GTK_DELETE_WORD_ENDS:
  142. commands[0] = (count > 0 ? TextEditCommand::DELETE_WORD_FORWARD
  143. : TextEditCommand::DELETE_WORD_BACKWARD);
  144. break;
  145. case GTK_DELETE_WORDS:
  146. if (count > 0) {
  147. commands[0] = TextEditCommand::MOVE_WORD_FORWARD;
  148. commands[1] = TextEditCommand::DELETE_WORD_BACKWARD;
  149. } else {
  150. commands[0] = TextEditCommand::MOVE_WORD_BACKWARD;
  151. commands[1] = TextEditCommand::DELETE_WORD_FORWARD;
  152. }
  153. break;
  154. case GTK_DELETE_DISPLAY_LINES:
  155. commands[0] = TextEditCommand::MOVE_TO_BEGINNING_OF_LINE;
  156. commands[1] = TextEditCommand::DELETE_TO_END_OF_LINE;
  157. break;
  158. case GTK_DELETE_DISPLAY_LINE_ENDS:
  159. commands[0] = (count > 0 ? TextEditCommand::DELETE_TO_END_OF_LINE
  160. : TextEditCommand::DELETE_TO_BEGINNING_OF_LINE);
  161. break;
  162. case GTK_DELETE_PARAGRAPH_ENDS:
  163. commands[0] =
  164. (count > 0 ? TextEditCommand::DELETE_TO_END_OF_PARAGRAPH
  165. : TextEditCommand::DELETE_TO_BEGINNING_OF_PARAGRAPH);
  166. break;
  167. case GTK_DELETE_PARAGRAPHS:
  168. commands[0] = TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH;
  169. commands[1] = TextEditCommand::DELETE_TO_END_OF_PARAGRAPH;
  170. break;
  171. default:
  172. // GTK_DELETE_WHITESPACE has no corresponding editor command.
  173. return;
  174. }
  175. GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
  176. if (count < 0)
  177. count = -count;
  178. for (; count > 0; --count) {
  179. for (auto& command : commands) {
  180. if (command != TextEditCommand::INVALID_COMMAND)
  181. owner->EditCommandMatched(command, std::string());
  182. }
  183. }
  184. }
  185. void GtkKeyBindingsHandler::InsertAtCursor(GtkTextView* text_view,
  186. const gchar* str) {
  187. if (str && *str) {
  188. GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::INSERT_TEXT,
  189. str);
  190. }
  191. }
  192. void GtkKeyBindingsHandler::MoveCursor(GtkTextView* text_view,
  193. GtkMovementStep step,
  194. gint count,
  195. gboolean extend_selection) {
  196. if (!count)
  197. return;
  198. TextEditCommand command;
  199. switch (step) {
  200. case GTK_MOVEMENT_LOGICAL_POSITIONS:
  201. if (extend_selection) {
  202. command =
  203. (count > 0 ? TextEditCommand::MOVE_FORWARD_AND_MODIFY_SELECTION
  204. : TextEditCommand::MOVE_BACKWARD_AND_MODIFY_SELECTION);
  205. } else {
  206. command = (count > 0 ? TextEditCommand::MOVE_FORWARD
  207. : TextEditCommand::MOVE_BACKWARD);
  208. }
  209. break;
  210. case GTK_MOVEMENT_VISUAL_POSITIONS:
  211. if (extend_selection) {
  212. command = (count > 0 ? TextEditCommand::MOVE_RIGHT_AND_MODIFY_SELECTION
  213. : TextEditCommand::MOVE_LEFT_AND_MODIFY_SELECTION);
  214. } else {
  215. command = (count > 0 ? TextEditCommand::MOVE_RIGHT
  216. : TextEditCommand::MOVE_LEFT);
  217. }
  218. break;
  219. case GTK_MOVEMENT_WORDS:
  220. if (extend_selection) {
  221. command =
  222. (count > 0 ? TextEditCommand::MOVE_WORD_RIGHT_AND_MODIFY_SELECTION
  223. : TextEditCommand::MOVE_WORD_LEFT_AND_MODIFY_SELECTION);
  224. } else {
  225. command = (count > 0 ? TextEditCommand::MOVE_WORD_RIGHT
  226. : TextEditCommand::MOVE_WORD_LEFT);
  227. }
  228. break;
  229. case GTK_MOVEMENT_DISPLAY_LINES:
  230. if (extend_selection) {
  231. command = (count > 0 ? TextEditCommand::MOVE_DOWN_AND_MODIFY_SELECTION
  232. : TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION);
  233. } else {
  234. command =
  235. (count > 0 ? TextEditCommand::MOVE_DOWN : TextEditCommand::MOVE_UP);
  236. }
  237. break;
  238. case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
  239. if (extend_selection) {
  240. command =
  241. (count > 0
  242. ? TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION
  243. : TextEditCommand::
  244. MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION);
  245. } else {
  246. command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_LINE
  247. : TextEditCommand::MOVE_TO_BEGINNING_OF_LINE);
  248. }
  249. break;
  250. case GTK_MOVEMENT_PARAGRAPH_ENDS:
  251. if (extend_selection) {
  252. command =
  253. (count > 0
  254. ? TextEditCommand::
  255. MOVE_TO_END_OF_PARAGRAPH_AND_MODIFY_SELECTION
  256. : TextEditCommand::
  257. MOVE_TO_BEGINNING_OF_PARAGRAPH_AND_MODIFY_SELECTION);
  258. } else {
  259. command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_PARAGRAPH
  260. : TextEditCommand::MOVE_TO_BEGINNING_OF_PARAGRAPH);
  261. }
  262. break;
  263. case GTK_MOVEMENT_PAGES:
  264. if (extend_selection) {
  265. command =
  266. (count > 0 ? TextEditCommand::MOVE_PAGE_DOWN_AND_MODIFY_SELECTION
  267. : TextEditCommand::MOVE_PAGE_UP_AND_MODIFY_SELECTION);
  268. } else {
  269. command = (count > 0 ? TextEditCommand::MOVE_PAGE_DOWN
  270. : TextEditCommand::MOVE_PAGE_UP);
  271. }
  272. break;
  273. case GTK_MOVEMENT_BUFFER_ENDS:
  274. if (extend_selection) {
  275. command =
  276. (count > 0
  277. ? TextEditCommand::MOVE_TO_END_OF_DOCUMENT_AND_MODIFY_SELECTION
  278. : TextEditCommand::
  279. MOVE_TO_BEGINNING_OF_DOCUMENT_AND_MODIFY_SELECTION);
  280. } else {
  281. command = (count > 0 ? TextEditCommand::MOVE_TO_END_OF_DOCUMENT
  282. : TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT);
  283. }
  284. break;
  285. default:
  286. // GTK_MOVEMENT_PARAGRAPHS and GTK_MOVEMENT_HORIZONTAL_PAGES have
  287. // no corresponding editor commands.
  288. return;
  289. }
  290. GtkKeyBindingsHandler* owner = GetHandlerOwner(text_view);
  291. if (count < 0)
  292. count = -count;
  293. for (; count > 0; --count)
  294. owner->EditCommandMatched(command, std::string());
  295. }
  296. void GtkKeyBindingsHandler::MoveViewport(GtkTextView* text_view,
  297. GtkScrollStep step,
  298. gint count) {
  299. // Not supported by Blink.
  300. }
  301. void GtkKeyBindingsHandler::PasteClipboard(GtkTextView* text_view) {
  302. GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::PASTE,
  303. std::string());
  304. }
  305. void GtkKeyBindingsHandler::SelectAll(GtkTextView* text_view, gboolean select) {
  306. GetHandlerOwner(text_view)->EditCommandMatched(
  307. select ? TextEditCommand::SELECT_ALL : TextEditCommand::UNSELECT,
  308. std::string());
  309. }
  310. void GtkKeyBindingsHandler::SetAnchor(GtkTextView* text_view) {
  311. GetHandlerOwner(text_view)->EditCommandMatched(TextEditCommand::SET_MARK,
  312. std::string());
  313. }
  314. void GtkKeyBindingsHandler::ToggleCursorVisible(GtkTextView* text_view) {
  315. // Not supported by Blink.
  316. }
  317. void GtkKeyBindingsHandler::ToggleOverwrite(GtkTextView* text_view) {
  318. // Not supported by Blink.
  319. }
  320. gboolean GtkKeyBindingsHandler::ShowHelp(GtkWidget* widget,
  321. GtkWidgetHelpType arg1) {
  322. // Just for disabling the default handler.
  323. return TRUE;
  324. }
  325. void GtkKeyBindingsHandler::MoveFocus(GtkWidget* widget,
  326. GtkDirectionType arg1) {
  327. // Just for disabling the default handler.
  328. }
  329. } // namespace gtk