[gnome-builder] Add emacs base support



commit ed3240bc0eb6b51fc68137f12c5f8ab2a06700c4
Author: Roberto Majadas <roberto majadas openshine com>
Date:   Sat Feb 7 21:45:57 2015 +0100

    Add emacs base support

 data/org.gnome.builder.editor.gschema.xml.in  |    5 +
 src/app/gb-application.c                      |   35 ++-
 src/editor/gb-source-view.c                   |   22 ++
 src/editor/gb-source-view.h                   |    2 +
 src/emacs/gb-source-emacs.c                   |  454 +++++++++++++++++++++++++
 src/emacs/gb-source-emacs.h                   |   65 ++++
 src/gnome-builder.mk                          |    5 +
 src/preferences/gb-preferences-page-emacs.c   |   94 +++++
 src/preferences/gb-preferences-page-emacs.h   |   55 +++
 src/preferences/gb-preferences-window.c       |    2 +
 src/resources/gnome-builder.gresource.xml     |    2 +
 src/resources/ui/gb-preferences-page-emacs.ui |   83 +++++
 src/resources/ui/gb-preferences-window.ui     |    9 +
 13 files changed, 831 insertions(+), 2 deletions(-)
---
diff --git a/data/org.gnome.builder.editor.gschema.xml.in b/data/org.gnome.builder.editor.gschema.xml.in
index f1069c9..55cdcbc 100644
--- a/data/org.gnome.builder.editor.gschema.xml.in
+++ b/data/org.gnome.builder.editor.gschema.xml.in
@@ -5,6 +5,11 @@
       <summary>Enable VIM keybindings</summary>
       <description>Whether or not VIM style keybindings should be used in the source code 
editor.</description>
     </key>
+    <key name="emacs-mode" type="b">
+      <default>false</default>
+      <summary>Enable EMACS keybindings</summary>
+      <description>Whether or not EMACS style keybindings should be used in the source code 
editor.</description>
+    </key>
     <key name="restore-insert-mark" type="b">
       <default>true</default>
       <summary>Restore last position</summary>
diff --git a/src/app/gb-application.c b/src/app/gb-application.c
index 266a0b4..d7f6271 100644
--- a/src/app/gb-application.c
+++ b/src/app/gb-application.c
@@ -338,7 +338,27 @@ gb_application_vim_mode_changed (GbApplication *self,
   g_return_if_fail (G_IS_SETTINGS (settings));
 
   if (g_settings_get_boolean (settings, "vim-mode"))
-    gb_application_load_keybindings (self, "vim");
+    {
+      g_settings_set_boolean (settings, "emacs-mode", FALSE);
+      gb_application_load_keybindings (self, "vim");
+    }
+  else
+    gb_application_load_keybindings (self, "default");
+}
+
+static void
+gb_application_emacs_mode_changed (GbApplication *self,
+                                 const gchar   *key,
+                                 GSettings     *settings)
+{
+  g_return_if_fail (GB_IS_APPLICATION (self));
+  g_return_if_fail (G_IS_SETTINGS (settings));
+
+  if (g_settings_get_boolean (settings, "emacs-mode"))
+    {
+      g_settings_set_boolean (settings, "vim-mode", FALSE);
+      gb_application_load_keybindings (self, "emacs");
+    }
   else
     gb_application_load_keybindings (self, "default");
 }
@@ -355,7 +375,18 @@ gb_application_register_keybindings (GbApplication *self)
                            G_CALLBACK (gb_application_vim_mode_changed),
                            self,
                            G_CONNECT_SWAPPED);
-  gb_application_vim_mode_changed (self, NULL, self->priv->editor_settings);
+  g_signal_connect_object (self->priv->editor_settings,
+                           "changed::emacs-mode",
+                           G_CALLBACK (gb_application_emacs_mode_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+  if (g_settings_get_boolean(self->priv->editor_settings, "vim-mode") == TRUE)
+    gb_application_vim_mode_changed (self, NULL, self->priv->editor_settings);
+  else if (g_settings_get_boolean(self->priv->editor_settings, "emacs-mode") == TRUE)
+    gb_application_emacs_mode_changed (self, NULL, self->priv->editor_settings);
+  else
+    gb_application_vim_mode_changed (self, NULL, self->priv->editor_settings);
+
 }
 
 static GbWorkbench *
diff --git a/src/editor/gb-source-view.c b/src/editor/gb-source-view.c
index 4268105..47cb7ec 100644
--- a/src/editor/gb-source-view.c
+++ b/src/editor/gb-source-view.c
@@ -44,6 +44,7 @@
 #include "gb-source-snippet-private.h"
 #include "gb-source-view.h"
 #include "gb-source-vim.h"
+#include "gb-source-emacs.h" 
 #include "gb-widget.h"
 
 enum {
@@ -60,6 +61,7 @@ struct _GbSourceViewPrivate
   GtkSourceCompletionProvider *snippets_provider;
   GtkSourceCompletionProvider *words_provider;
   GbSourceVim                 *vim;
+  GbSourceEmacs               *emacs;
   GtkCssProvider              *css_provider;
 
   GSettings                   *language_settings;
@@ -125,6 +127,14 @@ gb_source_view_get_vim (GbSourceView *view)
   return view->priv->vim;
 }
 
+GbSourceEmacs *
+gb_source_view_get_emacs (GbSourceView *view)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_VIEW (view), NULL);
+
+  return view->priv->emacs;
+}
+
 gboolean
 gb_source_view_get_insert_matching_brace (GbSourceView *view)
 {
@@ -235,6 +245,7 @@ gb_source_view_disconnect_settings (GbSourceView *view)
   g_settings_unbind (view, "tab-width");
   g_settings_unbind (view, "font-name");
   g_settings_unbind (view->priv->vim, "enabled");
+  g_settings_unbind (view->priv->emacs, "enabled");
 
   g_clear_object (&view->priv->language_settings);
   g_clear_object (&view->priv->editor_settings);
@@ -301,6 +312,8 @@ gb_source_view_connect_settings (GbSourceView *view)
                    buffer, "style-scheme-name", G_SETTINGS_BIND_GET);
   g_settings_bind (view->priv->editor_settings, "vim-mode",
                    view->priv->vim, "enabled", G_SETTINGS_BIND_GET);
+  g_settings_bind (view->priv->editor_settings, "emacs-mode",
+                   view->priv->emacs, "enabled", G_SETTINGS_BIND_GET);
   g_settings_bind (view->priv->editor_settings, "word-completion",
                    view, "enable-word-completion", G_SETTINGS_BIND_GET);
   g_settings_bind (view->priv->editor_settings, "show-line-numbers",
@@ -2074,6 +2087,7 @@ gb_source_view_finalize (GObject *object)
   g_clear_object (&priv->snippets_provider);
   g_clear_object (&priv->words_provider);
   g_clear_object (&priv->vim);
+  g_clear_object (&priv->emacs);
   g_clear_object (&priv->css_provider);
 
   G_OBJECT_CLASS (gb_source_view_parent_class)->finalize (object);
@@ -2431,6 +2445,14 @@ gb_source_view_init (GbSourceView *view)
                            G_CONNECT_SWAPPED);
 
   /*
+   * Setup Emacs integration.
+   */
+    view->priv->emacs = g_object_new (GB_TYPE_SOURCE_EMACS,
+                                      "enabled", FALSE,
+                                      "text-view", view,
+                                      NULL);
+
+  /*
    * We block completion when we are not focused so that two SourceViews
    * viewing the same GtkTextBuffer do not both show completion windows.
    */
diff --git a/src/editor/gb-source-view.h b/src/editor/gb-source-view.h
index c4a2869..e1b8bbf 100644
--- a/src/editor/gb-source-view.h
+++ b/src/editor/gb-source-view.h
@@ -24,6 +24,7 @@
 #include "gb-source-auto-indenter.h"
 #include "gb-source-snippet.h"
 #include "gb-source-vim.h"
+#include "gb-source-emacs.h"
 
 G_BEGIN_DECLS
 
@@ -88,6 +89,7 @@ void                  gb_source_view_set_overwrite_braces (GbSourceView
 void                  gb_source_view_set_show_shadow      (GbSourceView         *view,
                                                            gboolean              show_shadow);
 GbSourceVim          *gb_source_view_get_vim              (GbSourceView         *view);
+GbSourceEmacs        *gb_source_view_get_emacs            (GbSourceView         *view);
 
 G_END_DECLS
 
diff --git a/src/emacs/gb-source-emacs.c b/src/emacs/gb-source-emacs.c
new file mode 100644
index 0000000..a3fd310
--- /dev/null
+++ b/src/emacs/gb-source-emacs.c
@@ -0,0 +1,454 @@
+/* gb-source-emacs.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Roberto Majadas <roberto majadas openshine com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "emacs"
+
+#include <errno.h>
+#include <glib/gi18n.h>
+#include <gtksourceview/gtksource.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "gb-source-emacs.h"
+#include "gb-string.h"
+
+struct _GbSourceEmacsPrivate
+{
+  GtkTextView             *text_view;
+  GString                 *cmd;
+  guint                    enabled : 1;
+  guint                    connected : 1;
+  gulong                   key_press_event_handler;
+  gulong                   event_after_handler;
+  gulong                   key_release_event_handler;
+};
+
+enum
+{
+  PROP_0,
+  PROP_ENABLED,
+  PROP_TEXT_VIEW,
+  LAST_PROP
+};
+
+typedef enum
+{
+  GB_SOURCE_EMACS_COMMAND_FLAG_NONE,
+} GbSourceEmacsCommandFlags;
+
+typedef void (*GbSourceEmacsCommandFunc) (GbSourceEmacs           *emacs,
+                                          GRegex                  *matcher,
+                                          GbSourceEmacsCommandFlags flags
+                                          );
+
+typedef struct
+{
+  GbSourceEmacsCommandFunc   func;
+  GRegex                    *matcher;
+  GbSourceEmacsCommandFlags  flags;
+} GbSourceEmacsCommand;
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbSourceEmacs, gb_source_emacs, G_TYPE_OBJECT)
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+static GList *gCommands;
+
+GbSourceEmacs *
+gb_source_emacs_new (GtkTextView *text_view)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+
+  return g_object_new (GB_TYPE_SOURCE_EMACS,
+                       "text-view", text_view,
+                       NULL);
+}
+
+static void
+gb_source_emacs_cmd_exit_from_command_line  (GbSourceEmacs           *emacs,
+                                             GRegex                  *matcher,
+                                             GbSourceEmacsCommandFlags flags)
+{
+  GbSourceEmacsPrivate *priv = GB_SOURCE_EMACS (emacs)->priv;
+
+  if (priv->cmd != NULL)
+    g_string_free(priv->cmd, TRUE);
+  priv->cmd = g_string_new(NULL);
+}
+
+static void
+gb_source_emacs_cmd_open_file  (GbSourceEmacs           *emacs,
+                                GRegex                  *matcher,
+                                GbSourceEmacsCommandFlags flags)
+{
+  return;
+}
+
+static gboolean
+gb_source_emacs_eval_cmd (GbSourceEmacs *emacs)
+{
+  GbSourceEmacsPrivate *priv = GB_SOURCE_EMACS (emacs)->priv;
+  GMatchInfo *match_info;
+  GList *iter;
+
+  for (iter = gCommands; iter; iter = iter->next)
+    {
+      GbSourceEmacsCommand *cmd = iter->data;
+
+      g_regex_match (cmd->matcher, priv->cmd->str, 0, &match_info);
+      if (g_match_info_matches(match_info))
+        {
+          cmd->func (emacs, cmd->matcher, cmd->flags);
+          g_match_info_free (match_info);
+          if (priv->cmd != NULL)
+            g_string_free(priv->cmd, TRUE);
+          priv->cmd = g_string_new(NULL);
+          break;
+        }
+      g_match_info_free (match_info);
+    }
+
+  g_print(">>> %s\n", priv->cmd->str);
+  return TRUE;
+}
+
+static void
+gb_source_emacs_event_after_cb (GtkTextView *text_view,
+                                GdkEventKey *event,
+                                GbSourceEmacs *emacs)
+{
+  g_return_if_fail (GB_IS_SOURCE_EMACS (emacs));
+}
+
+static gboolean
+gb_source_emacs_key_press_event_cb (GtkTextView *text_view,
+                                    GdkEventKey *event,
+                                    GbSourceEmacs *emacs)
+{
+  GbSourceEmacsPrivate *priv = GB_SOURCE_EMACS (emacs)->priv;
+  gboolean eval_cmd = FALSE;
+
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+  g_return_val_if_fail (event, FALSE);
+  g_return_val_if_fail (GB_IS_SOURCE_EMACS (emacs), FALSE);
+
+  if ((event->keyval >= 0x041 && event->keyval <= 0x05a) || (event->keyval >= 0x061 && event->keyval <= 
0x07a))
+    {
+      if (event->state == (GDK_CONTROL_MASK | GDK_MOD1_MASK))
+        {
+          if (priv->cmd->len != 0 )
+            g_string_append_printf(priv->cmd, " ");
+          g_string_append_printf(priv->cmd, "C-M-%s", gdk_keyval_name(event->keyval));
+          eval_cmd = TRUE;
+        }
+      else if ((event->state & GDK_CONTROL_MASK) != 0)
+        {
+          if (priv->cmd->len != 0 )
+            g_string_append_printf(priv->cmd, " ");
+          g_string_append_printf(priv->cmd, "C-%s", gdk_keyval_name(event->keyval));
+          eval_cmd = TRUE;
+        }
+      else if ((event->state & GDK_MOD1_MASK) != 0)
+        {
+          if (priv->cmd->len != 0 )
+            g_string_append_printf(priv->cmd, " ");
+          g_string_append_printf(priv->cmd, "M-%s", gdk_keyval_name(event->keyval));
+          eval_cmd = TRUE;
+        }
+    }
+
+  if (eval_cmd)
+    return gb_source_emacs_eval_cmd(emacs);
+
+  return FALSE;
+}
+
+static gboolean
+gb_source_emacs_key_release_event_cb (GtkTextView *text_view,
+                                      GdkEventKey *event,
+                                      GbSourceEmacs *emacs)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+  g_return_val_if_fail (event, FALSE);
+  g_return_val_if_fail (GB_IS_SOURCE_EMACS (emacs), FALSE);
+
+  return FALSE;
+}
+
+static void
+gb_source_emacs_connect (GbSourceEmacs *emacs)
+{
+  g_return_if_fail (GB_IS_SOURCE_EMACS (emacs));
+  g_return_if_fail (!emacs->priv->connected);
+
+  emacs->priv->key_press_event_handler =
+    g_signal_connect_object (emacs->priv->text_view,
+                             "key-press-event",
+                             G_CALLBACK (gb_source_emacs_key_press_event_cb),
+                             emacs,
+                             0);
+
+  emacs->priv->event_after_handler =
+    g_signal_connect_object (emacs->priv->text_view,
+                             "event-after",
+                             G_CALLBACK (gb_source_emacs_event_after_cb),
+                             emacs,
+                             0);
+
+  emacs->priv->key_release_event_handler =
+    g_signal_connect_object (emacs->priv->text_view,
+                             "key-release-event",
+                             G_CALLBACK (gb_source_emacs_key_release_event_cb),
+                             emacs,
+                             0);
+
+  emacs->priv->connected = TRUE;
+}
+
+static void
+gb_source_emacs_disconnect (GbSourceEmacs *emacs)
+{
+  g_return_if_fail (GB_IS_SOURCE_EMACS (emacs));
+  g_return_if_fail (emacs->priv->connected);
+
+  g_signal_handler_disconnect (emacs->priv->text_view,
+                               emacs->priv->key_press_event_handler);
+  emacs->priv->key_press_event_handler = 0;
+
+  g_signal_handler_disconnect (emacs->priv->text_view,
+                               emacs->priv->event_after_handler);
+  emacs->priv->event_after_handler = 0;
+
+  g_signal_handler_disconnect (emacs->priv->text_view,
+                               emacs->priv->key_release_event_handler);
+  emacs->priv->key_release_event_handler = 0;
+  emacs->priv->connected = FALSE;
+}
+
+gboolean
+gb_source_emacs_get_enabled (GbSourceEmacs *emacs)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_EMACS (emacs), FALSE);
+
+  return emacs->priv->enabled;
+}
+
+void
+gb_source_emacs_set_enabled (GbSourceEmacs *emacs,
+                           gboolean     enabled)
+{
+  GbSourceEmacsPrivate *priv;
+
+  g_return_if_fail (GB_IS_SOURCE_EMACS (emacs));
+
+  priv = emacs->priv;
+
+  if (priv->enabled == enabled)
+    return;
+
+  if (enabled)
+    {
+
+      gb_source_emacs_connect (emacs);
+      priv->enabled = TRUE;
+    }
+  else
+    {
+      gb_source_emacs_disconnect (emacs);
+      priv->enabled = FALSE;
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (emacs), gParamSpecs [PROP_ENABLED]);
+}
+
+GtkWidget *
+gb_source_emacs_get_text_view (GbSourceEmacs *emacs)
+{
+  g_return_val_if_fail (GB_IS_SOURCE_EMACS (emacs), NULL);
+
+  return (GtkWidget *)emacs->priv->text_view;
+}
+
+static void
+gb_source_emacs_set_text_view (GbSourceEmacs *emacs,
+                             GtkTextView *text_view)
+{
+  GbSourceEmacsPrivate *priv;
+
+  g_return_if_fail (GB_IS_SOURCE_EMACS (emacs));
+  g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+  priv = emacs->priv;
+
+  if (priv->text_view == text_view)
+    return;
+
+  if (priv->text_view)
+    {
+      if (priv->enabled)
+        gb_source_emacs_disconnect (emacs);
+      g_object_remove_weak_pointer (G_OBJECT (priv->text_view),
+                                    (gpointer *)&priv->text_view);
+      priv->text_view = NULL;
+    }
+
+  if (text_view)
+    {
+      priv->text_view = text_view;
+      g_object_add_weak_pointer (G_OBJECT (text_view),
+                                 (gpointer *)&priv->text_view);
+      if (priv->enabled)
+        gb_source_emacs_connect (emacs);
+    }
+
+  g_object_notify_by_pspec (G_OBJECT (emacs), gParamSpecs [PROP_TEXT_VIEW]);
+}
+
+static void
+gb_source_emacs_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GbSourceEmacs *emacs = GB_SOURCE_EMACS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      g_value_set_boolean (value, gb_source_emacs_get_enabled (emacs));
+      break;
+
+    case PROP_TEXT_VIEW:
+      g_value_set_object (value, gb_source_emacs_get_text_view (emacs));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gb_source_emacs_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GbSourceEmacs *emacs = GB_SOURCE_EMACS (object);
+
+  switch (prop_id)
+    {
+    case PROP_ENABLED:
+      gb_source_emacs_set_enabled (emacs, g_value_get_boolean (value));
+      break;
+
+    case PROP_TEXT_VIEW:
+      gb_source_emacs_set_text_view (emacs, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gb_source_emacs_finalize (GObject *object)
+{
+       GbSourceEmacsPrivate *priv = GB_SOURCE_EMACS (object)->priv;
+       if (priv->text_view)
+    {
+      gb_source_emacs_disconnect (GB_SOURCE_EMACS (object));
+      g_object_remove_weak_pointer (G_OBJECT (priv->text_view),
+                                    (gpointer *)&priv->text_view);
+      priv->text_view = NULL;
+    }
+  if (priv->cmd != NULL)
+    g_string_free(priv->cmd, TRUE);
+
+       G_OBJECT_CLASS (gb_source_emacs_parent_class)->finalize (object);
+}
+
+static void
+gb_source_emacs_class_register_command (GbSourceEmacsClass          *klass,
+                                        GRegex                      *matcher,
+                                        GbSourceEmacsCommandFlags   flags,
+                                        GbSourceEmacsCommandFunc    func)
+{
+  GbSourceEmacsCommand *cmd;
+
+  g_assert (GB_IS_SOURCE_EMACS_CLASS (klass));
+
+  cmd = g_new0 (GbSourceEmacsCommand, 1);
+  cmd->matcher = matcher;
+  cmd->func = func;
+  cmd->flags = flags;
+
+  gCommands = g_list_append(gCommands, cmd);
+}
+
+
+static void
+gb_source_emacs_class_init (GbSourceEmacsClass *klass)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = gb_source_emacs_finalize;
+  object_class->get_property = gb_source_emacs_get_property;
+  object_class->set_property = gb_source_emacs_set_property;
+
+  gParamSpecs [PROP_ENABLED] =
+    g_param_spec_boolean ("enabled",
+                          _("Enabled"),
+                          _("If the EMACS engine is enabled."),
+                          FALSE,
+                          (G_PARAM_READWRITE |
+                           G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_ENABLED,
+                                   gParamSpecs [PROP_ENABLED]);
+
+  gParamSpecs [PROP_TEXT_VIEW] =
+    g_param_spec_object ("text-view",
+                         _("Text View"),
+                         _("The text view the EMACS engine is managing."),
+                         GTK_TYPE_TEXT_VIEW,
+                         (G_PARAM_READWRITE |
+                          G_PARAM_CONSTRUCT_ONLY |
+                          G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (object_class, PROP_TEXT_VIEW,
+                                   gParamSpecs [PROP_TEXT_VIEW]);
+
+  /* Register emacs commands */
+  gb_source_emacs_class_register_command (klass,
+                                          g_regex_new("C-g$", 0, 0, NULL),
+                                          GB_SOURCE_EMACS_COMMAND_FLAG_NONE,
+                                          gb_source_emacs_cmd_exit_from_command_line);
+  gb_source_emacs_class_register_command (klass,
+                                          g_regex_new("^C-x C-s$", 0, 0, NULL),
+                                          GB_SOURCE_EMACS_COMMAND_FLAG_NONE,
+                                          gb_source_emacs_cmd_open_file);
+}
+
+static void
+gb_source_emacs_init (GbSourceEmacs *emacs)
+{
+  emacs->priv = gb_source_emacs_get_instance_private (emacs);
+  emacs->priv->enabled = FALSE;
+  emacs->priv->cmd = g_string_new(NULL);
+}
+
+
diff --git a/src/emacs/gb-source-emacs.h b/src/emacs/gb-source-emacs.h
new file mode 100644
index 0000000..ebefa57
--- /dev/null
+++ b/src/emacs/gb-source-emacs.h
@@ -0,0 +1,65 @@
+/* gb-source-emacs.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015 Roberto Majadas <roberto majadas openshine com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SOURCE_EMACS_H
+#define GB_SOURCE_EMACS_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_SOURCE_EMACS            (gb_source_emacs_get_type())
+#define GB_SOURCE_EMACS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SOURCE_EMACS, 
GbSourceEmacs))
+#define GB_SOURCE_EMACS_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), GB_TYPE_SOURCE_EMACS, 
GbSourceEmacs const))
+#define GB_SOURCE_EMACS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GB_TYPE_SOURCE_EMACS, 
GbSourceEmacsClass))
+#define GB_IS_SOURCE_EMACS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GB_TYPE_SOURCE_EMACS))
+#define GB_IS_SOURCE_EMACS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GB_TYPE_SOURCE_EMACS))
+#define GB_SOURCE_EMACS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GB_TYPE_SOURCE_EMACS, 
GbSourceEmacsClass))
+
+typedef struct _GbSourceEmacs        GbSourceEmacs;
+typedef struct _GbSourceEmacsClass   GbSourceEmacsClass;
+typedef struct _GbSourceEmacsPrivate GbSourceEmacsPrivate;
+
+struct _GbSourceEmacs
+{
+  GObject parent;
+
+  /*< private >*/
+  GbSourceEmacsPrivate *priv;
+};
+
+struct _GbSourceEmacsClass
+{
+  GObjectClass parent_class;
+
+  gpointer _padding1;
+  gpointer _padding2;
+  gpointer _padding3;
+};
+
+GType            gb_source_emacs_get_type          (void);
+GbSourceEmacs    *gb_source_emacs_new              (GtkTextView     *text_view);
+gboolean         gb_source_emacs_get_enabled       (GbSourceEmacs     *emacs);
+void             gb_source_emacs_set_enabled       (GbSourceEmacs     *emacs,
+                                                    gboolean         enabled);
+
+
+G_END_DECLS
+
+#endif /* GB_SOURCE_EMACS_H */
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index 60d1172..fb6844e 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -97,6 +97,8 @@ libgnome_builder_la_SOURCES = \
        src/editor/gb-source-search-highlighter.h \
        src/editor/gb-source-view.c \
        src/editor/gb-source-view.h \
+       src/emacs/gb-source-emacs.c \
+       src/emacs/gb-source-emacs.h \
        src/fuzzy/fuzzy.c \
        src/fuzzy/fuzzy.h \
        src/gca/gca-diagnostics.c \
@@ -131,6 +133,8 @@ libgnome_builder_la_SOURCES = \
        src/navigation/gb-navigation-list.h \
        src/preferences/gb-preferences-page-editor.c \
        src/preferences/gb-preferences-page-editor.h \
+       src/preferences/gb-preferences-page-emacs.c \
+       src/preferences/gb-preferences-page-emacs.h \
        src/preferences/gb-preferences-page-git.c \
        src/preferences/gb-preferences-page-git.h \
        src/preferences/gb-preferences-page-language.c \
@@ -236,6 +240,7 @@ libgnome_builder_la_CFLAGS = \
        -I$(top_srcdir)/src/dialogs \
        -I$(top_srcdir)/src/documents \
        -I$(top_srcdir)/src/editor \
+       -I$(top_srcdir)/src/emacs  \
        -I$(top_srcdir)/src/fuzzy \
        -I$(top_srcdir)/src/gca \
        -I$(top_srcdir)/src/gd \
diff --git a/src/preferences/gb-preferences-page-emacs.c b/src/preferences/gb-preferences-page-emacs.c
new file mode 100644
index 0000000..bc16f1e
--- /dev/null
+++ b/src/preferences/gb-preferences-page-emacs.c
@@ -0,0 +1,94 @@
+/* gb-preferences-page-emacs.c
+ *
+ * Copyright (C) 2015 Roberto Majadas <roberto majadas openshine com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "prefs-page-editor"
+
+#include <glib/gi18n.h>
+
+#include "gb-preferences-page-emacs.h"
+
+struct _GbPreferencesPageEmacsPrivate
+{
+  GSettings             *editor_settings;
+
+  /* Widgets owned by Template */
+  GtkSwitch             *emacs_mode_switch;
+
+  /* Template widgets used for filtering */
+  GtkWidget             *emacs_container;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPageEmacs, gb_preferences_page_emacs,
+                            GB_TYPE_PREFERENCES_PAGE)
+
+static void
+gb_preferences_page_emacs_constructed (GObject *object)
+{
+  GbPreferencesPageEmacsPrivate *priv;
+  GbPreferencesPageEmacs *emacs = (GbPreferencesPageEmacs *)object;
+
+  g_return_if_fail (GB_IS_PREFERENCES_PAGE_EMACS (emacs));
+
+  priv = emacs->priv;
+
+  priv->editor_settings = g_settings_new ("org.gnome.builder.editor");
+
+  g_settings_bind (priv->editor_settings, "emacs-mode",
+                   priv->emacs_mode_switch, "active",
+                   G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+gb_preferences_page_emacs_finalize (GObject *object)
+{
+  GbPreferencesPageEmacsPrivate *priv = GB_PREFERENCES_PAGE_EMACS (object)->priv;
+
+  g_clear_object (&priv->editor_settings);
+
+  G_OBJECT_CLASS (gb_preferences_page_emacs_parent_class)->finalize (object);
+}
+
+static void
+gb_preferences_page_emacs_class_init (GbPreferencesPageEmacsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gb_preferences_page_emacs_finalize;
+  object_class->constructed = gb_preferences_page_emacs_constructed;
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/builder/ui/gb-preferences-page-emacs.ui");
+
+  gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageEmacs, emacs_mode_switch);
+  gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageEmacs, emacs_container);
+}
+
+static void
+gb_preferences_page_emacs_init (GbPreferencesPageEmacs *self)
+{
+  self->priv = gb_preferences_page_emacs_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
+                                               _("emacs modal"),
+                                               self->priv->emacs_container,
+                                               self->priv->emacs_mode_switch,
+                                               NULL);
+}
diff --git a/src/preferences/gb-preferences-page-emacs.h b/src/preferences/gb-preferences-page-emacs.h
new file mode 100644
index 0000000..1153b0e
--- /dev/null
+++ b/src/preferences/gb-preferences-page-emacs.h
@@ -0,0 +1,55 @@
+/* gb-preferences-page-emacs.h
+ *
+ * Copyright (C) 2015 Roberto Majadas <roberto majadas openshine com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_PREFERENCES_PAGE_EMACS_H
+#define GB_PREFERENCES_PAGE_EMACS_H
+
+#include "gb-preferences-page.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_PREFERENCES_PAGE_EMACS            (gb_preferences_page_emacs_get_type())
+#define GB_PREFERENCES_PAGE_EMACS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_EMACS, GbPreferencesPageEmacs))
+#define GB_PREFERENCES_PAGE_EMACS_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_EMACS, GbPreferencesPageEmacs const))
+#define GB_PREFERENCES_PAGE_EMACS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
GB_TYPE_PREFERENCES_PAGE_EMACS, GbPreferencesPageEmacsClass))
+#define GB_IS_PREFERENCES_PAGE_EMACS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GB_TYPE_PREFERENCES_PAGE_EMACS))
+#define GB_IS_PREFERENCES_PAGE_EMACS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
GB_TYPE_PREFERENCES_PAGE_EMACS))
+#define GB_PREFERENCES_PAGE_EMACS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
GB_TYPE_PREFERENCES_PAGE_EMACS, GbPreferencesPageEmacsClass))
+
+typedef struct _GbPreferencesPageEmacs        GbPreferencesPageEmacs;
+typedef struct _GbPreferencesPageEmacsClass   GbPreferencesPageEmacsClass;
+typedef struct _GbPreferencesPageEmacsPrivate GbPreferencesPageEmacsPrivate;
+
+struct _GbPreferencesPageEmacs
+{
+  GbPreferencesPage parent;
+
+  /*< private >*/
+  GbPreferencesPageEmacsPrivate *priv;
+};
+
+struct _GbPreferencesPageEmacsClass
+{
+  GbPreferencesPageClass parent;
+};
+
+GType                     gb_preferences_page_emacs_get_type (void);
+
+G_END_DECLS
+
+#endif /* GB_PREFERENCES_PAGE_EMACS_H */
\ No newline at end of file
diff --git a/src/preferences/gb-preferences-window.c b/src/preferences/gb-preferences-window.c
index 20d8ab6..98f417b 100644
--- a/src/preferences/gb-preferences-window.c
+++ b/src/preferences/gb-preferences-window.c
@@ -25,6 +25,7 @@
 #include <glib/gi18n.h>
 
 #include "gb-preferences-page-editor.h"
+#include "gb-preferences-page-emacs.h"
 #include "gb-preferences-page-git.h"
 #include "gb-preferences-page-language.h"
 #include "gb-preferences-page-vim.h"
@@ -337,6 +338,7 @@ gb_preferences_window_class_init (GbPreferencesWindowClass *klass)
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_EDITOR);
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_LANGUAGE);
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_VIM);
+  g_type_ensure (GB_TYPE_PREFERENCES_PAGE_EMACS);
 }
 
 static void
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index 9b24dee..c6c6c57 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -15,6 +15,7 @@
     <file>js/markdown-view.js</file>
 
     <file>keybindings/default.ini</file>
+    <file>keybindings/emacs.ini</file>
     <file>keybindings/vim.ini</file>
 
     <file>language/defaults.ini</file>
@@ -45,6 +46,7 @@
     <file>ui/gb-html-view.ui</file>
     <file>ui/gb-preferences-window.ui</file>
     <file>ui/gb-preferences-page-editor.ui</file>
+    <file>ui/gb-preferences-page-emacs.ui</file>
     <file>ui/gb-preferences-page-git.ui</file>
     <file>ui/gb-preferences-page-language.ui</file>
     <file>ui/gb-preferences-page-vim.ui</file>
diff --git a/src/resources/keybindings/emacs.ini b/src/resources/keybindings/emacs.ini
new file mode 100644
index 0000000..e69de29
diff --git a/src/resources/ui/gb-preferences-page-emacs.ui b/src/resources/ui/gb-preferences-page-emacs.ui
new file mode 100644
index 0000000..90cb6a7
--- /dev/null
+++ b/src/resources/ui/gb-preferences-page-emacs.ui
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <template class="GbPreferencesPageEmacs" parent="GbPreferencesPage">
+    <child>
+      <object class="GtkBox">
+        <property name="visible">true</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkGrid">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="hexpand">True</property>
+            <property name="row_spacing">12</property>
+            <property name="column_spacing">12</property>
+            <child>
+              <object class="GtkSwitch" id="emacs_mode_switch">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="valign">center</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+
+            <child>
+              <object class="GtkBox" id="emacs_container">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="hexpand">True</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;EMACS Modal 
Editing&lt;/b&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="xalign">0</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Use EMACS style modal editing in the source 
code editor.</property>
+                    <property name="xalign">0</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/src/resources/ui/gb-preferences-window.ui b/src/resources/ui/gb-preferences-window.ui
index 7f11d6d..a9fcdfc 100644
--- a/src/resources/ui/gb-preferences-window.ui
+++ b/src/resources/ui/gb-preferences-window.ui
@@ -141,6 +141,15 @@
                 <property name="title" translatable="yes">Vim</property>
               </packing>
             </child>
+            <child>
+              <object class="GbPreferencesPageEmacs" id="emacs_page">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="name">emacs</property>
+                <property name="title" translatable="yes">Emacs</property>
+              </packing>
+            </child>
           </object>
         </child>
       </object>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]