[gnome-builder] gb-source-vim: Add support for vim scrolloff



commit c4f71f75acb8681847ce5116d03c413cf9997b7e
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Tue Jan 6 00:01:02 2015 +0100

    gb-source-vim: Add support for vim scrolloff
    
    Vim scrolloff allow the user to set a margin of a number of lines
    while moving through the buffer.
    Add support to it and add a setting to let the user adjust the number
    of lines.
    
    One downside of the implementation is that movement commands scroll
    horizontally not just the needed movement but also scrolls making the
    word position the left as possible. This is noticed when the scroll view
    has horizontal scrolling and the user moves backward in word motions in
    the words that are normally outside the view when the view is scrolled
    maximum to the left.
    
    As a detail, a scrolloff greater than half of the number of lines in the
    buffer keeps the current line always in the middle.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=742442

 configure.ac                                     |    1 +
 data/gsettings.mk                                |    1 +
 data/org.gnome.builder.editor.vim.gschema.xml.in |    8 +
 src/gnome-builder.mk                             |    2 +
 src/preferences/gb-preferences-page-vim.c        |   94 +++++++
 src/preferences/gb-preferences-page-vim.h        |   55 ++++
 src/preferences/gb-preferences-page.c            |   22 ++
 src/preferences/gb-preferences-page.h            |   17 +-
 src/preferences/gb-preferences-window.c          |   38 +++-
 src/resources/gnome-builder.gresource.xml        |    1 +
 src/resources/ui/gb-preferences-page-vim.ui      |   90 +++++++
 src/resources/ui/gb-preferences-window.ui        |    9 +
 src/vim/gb-source-vim.c                          |  296 +++++++++++++++++-----
 13 files changed, 559 insertions(+), 75 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b4e0b7f..23eb9ee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -51,6 +51,7 @@ AC_CONFIG_FILES([
        data/icons/Makefile
        data/org.gnome.builder.editor.gschema.xml
        data/org.gnome.builder.editor.language.gschema.xml
+       data/org.gnome.builder.editor.vim.gschema.xml
 ])
 
 # Last change to configure things and write configuration files.
diff --git a/data/gsettings.mk b/data/gsettings.mk
index e15f2b6..fbd3e0b 100644
--- a/data/gsettings.mk
+++ b/data/gsettings.mk
@@ -1,5 +1,6 @@
 gsettingsschema_in_files = \
        data/org.gnome.builder.editor.gschema.xml.in \
+       data/org.gnome.builder.editor.vim.gschema.xml.in \
        data/org.gnome.builder.editor.language.gschema.xml.in
 
 gsettings_SCHEMAS = $(gsettingsschema_in_files:.xml.in=.xml)
diff --git a/data/org.gnome.builder.editor.vim.gschema.xml.in 
b/data/org.gnome.builder.editor.vim.gschema.xml.in
new file mode 100644
index 0000000..9f2a829
--- /dev/null
+++ b/data/org.gnome.builder.editor.vim.gschema.xml.in
@@ -0,0 +1,8 @@
+<schemalist>
+  <schema id="org.gnome.builder.editor.vim" path="/org/gnome/builder/editor/vim/" 
gettext-domain="gnome-builder">
+    <key name="scroll-off" type="i">
+      <range min="0" max="999"/>
+      <default>0</default>
+    </key>
+  </schema>
+</schemalist>
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index bcdc5f9..6c877ac 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -141,6 +141,8 @@ libgnome_builder_la_SOURCES = \
        src/preferences/gb-preferences-page-git.h \
        src/preferences/gb-preferences-page-language.c \
        src/preferences/gb-preferences-page-language.h \
+       src/preferences/gb-preferences-page-vim.c \
+       src/preferences/gb-preferences-page-vim.h \
        src/preferences/gb-preferences-page.c \
        src/preferences/gb-preferences-page.h \
        src/preferences/gb-preferences-window.c \
diff --git a/src/preferences/gb-preferences-page-vim.c b/src/preferences/gb-preferences-page-vim.c
new file mode 100644
index 0000000..deb642d
--- /dev/null
+++ b/src/preferences/gb-preferences-page-vim.c
@@ -0,0 +1,94 @@
+/* gb-preferences-page-vim.c
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * 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-vim.h"
+
+struct _GbPreferencesPageVimPrivate
+{
+  GSettings             *settings;
+
+  /* Widgets owned by Template */
+  GtkSpinButton         *scroll_off_spin;
+
+  /* Template widgets used for filtering */
+  GtkWidget             *scroll_off_container;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPageVim, gb_preferences_page_vim,
+                            GB_TYPE_PREFERENCES_PAGE)
+
+static void
+gb_preferences_page_vim_constructed (GObject *object)
+{
+  GbPreferencesPageVimPrivate *priv;
+  GbPreferencesPageVim *vim = (GbPreferencesPageVim *)object;
+
+  g_return_if_fail (GB_IS_PREFERENCES_PAGE_VIM (vim));
+
+  priv = vim->priv;
+
+  priv->settings = g_settings_new ("org.gnome.builder.editor.vim");
+
+  g_settings_bind (priv->settings, "scroll-off", priv->scroll_off_spin, "value",
+                   G_SETTINGS_BIND_DEFAULT);
+}
+
+static void
+gb_preferences_page_vim_finalize (GObject *object)
+{
+  GbPreferencesPageVimPrivate *priv = GB_PREFERENCES_PAGE_VIM (object)->priv;
+
+  g_clear_object (&priv->settings);
+
+  G_OBJECT_CLASS (gb_preferences_page_vim_parent_class)->finalize (object);
+}
+
+static void
+gb_preferences_page_vim_class_init (GbPreferencesPageVimClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+  object_class->finalize = gb_preferences_page_vim_finalize;
+  object_class->constructed = gb_preferences_page_vim_constructed;
+
+  gtk_widget_class_set_template_from_resource (widget_class,
+                                               "/org/gnome/builder/ui/gb-preferences-page-vim.ui");
+
+  gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageVim, scroll_off_spin);
+
+  gtk_widget_class_bind_template_child_private (widget_class, GbPreferencesPageVim, scroll_off_container);
+}
+
+static void
+gb_preferences_page_vim_init (GbPreferencesPageVim *self)
+{
+  self->priv = gb_preferences_page_vim_get_instance_private (self);
+
+  gtk_widget_init_template (GTK_WIDGET (self));
+
+  gb_preferences_page_set_keywords_for_widget (GB_PREFERENCES_PAGE (self),
+                                               _("lines margin scrolloff scroll off"),
+                                               self->priv->scroll_off_container,
+                                               self->priv->scroll_off_spin,
+                                               NULL);
+}
diff --git a/src/preferences/gb-preferences-page-vim.h b/src/preferences/gb-preferences-page-vim.h
new file mode 100644
index 0000000..f3a7b0e
--- /dev/null
+++ b/src/preferences/gb-preferences-page-vim.h
@@ -0,0 +1,55 @@
+/* gb-preferences-page-vim.h
+ *
+ * Copyright (C) 2015 Carlos Soriano <csoriano gnome org>
+ *
+ * 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_VIM_H
+#define GB_PREFERENCES_PAGE_VIM_H
+
+#include "gb-preferences-page.h"
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_PREFERENCES_PAGE_VIM            (gb_preferences_page_vim_get_type())
+#define GB_PREFERENCES_PAGE_VIM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVim))
+#define GB_PREFERENCES_PAGE_VIM_CONST(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVim const))
+#define GB_PREFERENCES_PAGE_VIM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVimClass))
+#define GB_IS_PREFERENCES_PAGE_VIM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
GB_TYPE_PREFERENCES_PAGE_VIM))
+#define GB_IS_PREFERENCES_PAGE_VIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  
GB_TYPE_PREFERENCES_PAGE_VIM))
+#define GB_PREFERENCES_PAGE_VIM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  
GB_TYPE_PREFERENCES_PAGE_VIM, GbPreferencesPageVimClass))
+
+typedef struct _GbPreferencesPageVim        GbPreferencesPageVim;
+typedef struct _GbPreferencesPageVimClass   GbPreferencesPageVimClass;
+typedef struct _GbPreferencesPageVimPrivate GbPreferencesPageVimPrivate;
+
+struct _GbPreferencesPageVim
+{
+  GbPreferencesPage parent;
+
+  /*< private >*/
+  GbPreferencesPageVimPrivate *priv;
+};
+
+struct _GbPreferencesPageVimClass
+{
+  GbPreferencesPageClass parent;
+};
+
+GType                     gb_preferences_page_vim_get_type (void);
+
+G_END_DECLS
+
+#endif /* GB_PREFERENCES_PAGE_VIM_H */
diff --git a/src/preferences/gb-preferences-page.c b/src/preferences/gb-preferences-page.c
index aef24e5..fbc07d1 100644
--- a/src/preferences/gb-preferences-page.c
+++ b/src/preferences/gb-preferences-page.c
@@ -28,6 +28,7 @@ struct _GbPreferencesPagePrivate
 {
   GHashTable *widgets;
   gchar      *title;
+  gboolean    active;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesPage, gb_preferences_page,
@@ -155,6 +156,23 @@ gb_preferences_page_set_title (GbPreferencesPage *page,
     }
 }
 
+gboolean
+gb_preferences_page_get_active (GbPreferencesPage *page)
+{
+  g_return_val_if_fail (GB_IS_PREFERENCES_PAGE (page), FALSE);
+
+  return page->priv->active;
+}
+
+void
+gb_preferences_page_set_active (GbPreferencesPage *page,
+                                gboolean          active)
+{
+  g_return_if_fail (GB_IS_PREFERENCES_PAGE (page));
+
+  page->priv->active = active;
+}
+
 static void
 gb_preferences_page_finalize (GObject *object)
 {
@@ -230,4 +248,8 @@ gb_preferences_page_init (GbPreferencesPage *self)
   self->priv = gb_preferences_page_get_instance_private (self);
   self->priv->widgets = g_hash_table_new_full (g_direct_hash, g_direct_equal,
                                                NULL, NULL);
+  /* Make it active by default. If some page has to be disabled
+   * let the preferences window make it disabled.
+   */
+  self->priv->active = TRUE;
 }
diff --git a/src/preferences/gb-preferences-page.h b/src/preferences/gb-preferences-page.h
index e475859..5d4f93a 100644
--- a/src/preferences/gb-preferences-page.h
+++ b/src/preferences/gb-preferences-page.h
@@ -48,13 +48,16 @@ struct _GbPreferencesPageClass
   GtkBinClass parent;
 };
 
-GType gb_preferences_page_get_type                (void);
-guint gb_preferences_page_set_keywords            (GbPreferencesPage   *page,
-                                                   const gchar * const *keywords);
-void  gb_preferences_page_set_keywords_for_widget (GbPreferencesPage   *page,
-                                                   const gchar         *keywords,
-                                                   GtkWidget           *first_widget,
-                                                   ...) G_GNUC_NULL_TERMINATED;
+GType    gb_preferences_page_get_type                (void);
+guint    gb_preferences_page_set_keywords            (GbPreferencesPage   *page,
+                                                      const gchar * const *keywords);
+void     gb_preferences_page_set_keywords_for_widget (GbPreferencesPage   *page,
+                                                      const gchar         *keywords,
+                                                      GtkWidget           *first_widget,
+                                                      ...) G_GNUC_NULL_TERMINATED;
+gboolean gb_preferences_page_get_active              (GbPreferencesPage   *page);
+void     gb_preferences_page_set_active              (GbPreferencesPage   *page,
+                                                      gboolean             active);
 
 G_END_DECLS
 
diff --git a/src/preferences/gb-preferences-window.c b/src/preferences/gb-preferences-window.c
index d97a276..b91ed79 100644
--- a/src/preferences/gb-preferences-window.c
+++ b/src/preferences/gb-preferences-window.c
@@ -27,6 +27,8 @@
 #include "gb-preferences-page-editor.h"
 #include "gb-preferences-page-git.h"
 #include "gb-preferences-page-language.h"
+#include "gb-preferences-page-vim.h"
+#include "gb-preferences-page.h"
 #include "gb-preferences-window.h"
 #include "gb-widget.h"
 
@@ -36,6 +38,10 @@ struct _GbPreferencesWindowPrivate
   GtkSearchEntry  *search_entry;
   GtkSearchBar    *search_bar;
   GtkStack        *stack;
+
+  GtkWidget       *vim_page;
+
+  GSettings       *editor_settings;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE (GbPreferencesWindow, gb_preferences_window,
@@ -111,7 +117,8 @@ gb_preferences_window_search_changed (GbPreferencesWindow *window,
       if (0 == gb_preferences_page_set_keywords (page, (const gchar * const *)keywords))
         gtk_widget_set_visible (GTK_WIDGET (page), FALSE);
       else
-        gtk_widget_set_visible (GTK_WIDGET (page), TRUE);
+        gtk_widget_set_visible (GTK_WIDGET (page),
+                                gb_preferences_page_get_active (page));
     }
 
   g_list_free (pages);
@@ -247,6 +254,10 @@ gb_preferences_window_constructed (GObject *object)
 static void
 gb_preferences_window_finalize (GObject *object)
 {
+  GbPreferencesWindowPrivate *priv = GB_PREFERENCES_WINDOW (object)->priv;
+
+  g_clear_object (&priv->editor_settings);
+
   G_OBJECT_CLASS (gb_preferences_window_parent_class)->finalize (object);
 }
 
@@ -277,6 +288,22 @@ gb_preferences_window_set_property (GObject      *object,
 }
 
 static void
+gb_prefereces_window_vim_mode_changed (GbPreferencesWindow *window,
+                                       const gchar         *key,
+                                       GSettings           *settings)
+{
+  gboolean active;
+
+  g_return_if_fail (GB_IS_PREFERENCES_WINDOW (window));
+  g_return_if_fail (G_IS_SETTINGS (settings));
+
+  active = g_settings_get_boolean (settings, "vim-mode");
+  gtk_widget_set_visible (window->priv->vim_page, active);
+  gb_preferences_page_set_active (GB_PREFERENCES_PAGE (window->priv->vim_page),
+                                  active);
+}
+
+static void
 gb_preferences_window_class_init (GbPreferencesWindowClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -311,6 +338,7 @@ gb_preferences_window_class_init (GbPreferencesWindowClass *klass)
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_GIT);
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_EDITOR);
   g_type_ensure (GB_TYPE_PREFERENCES_PAGE_LANGUAGE);
+  g_type_ensure (GB_TYPE_PREFERENCES_PAGE_VIM);
 }
 
 static void
@@ -319,4 +347,12 @@ gb_preferences_window_init (GbPreferencesWindow *self)
   self->priv = gb_preferences_window_get_instance_private (self);
 
   gtk_widget_init_template (GTK_WIDGET (self));
+
+  self->priv->editor_settings = g_settings_new ("org.gnome.builder.editor");
+  g_signal_connect_object (self->priv->editor_settings,
+                           "changed::vim-mode",
+                           G_CALLBACK (gb_prefereces_window_vim_mode_changed),
+                           self,
+                           G_CONNECT_SWAPPED);
+  gb_prefereces_window_vim_mode_changed (self, NULL, self->priv->editor_settings);
 }
diff --git a/src/resources/gnome-builder.gresource.xml b/src/resources/gnome-builder.gresource.xml
index eb7d01e..5bbd5a9 100644
--- a/src/resources/gnome-builder.gresource.xml
+++ b/src/resources/gnome-builder.gresource.xml
@@ -44,6 +44,7 @@
     <file>ui/gb-preferences-page-editor.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>
     <file>ui/gb-search-box.ui</file>
     <file>ui/gb-search-display.ui</file>
     <file>ui/gb-workbench.ui</file>
diff --git a/src/resources/ui/gb-preferences-page-vim.ui b/src/resources/ui/gb-preferences-page-vim.ui
new file mode 100644
index 0000000..e55f692
--- /dev/null
+++ b/src/resources/ui/gb-preferences-page-vim.ui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.8 -->
+  <template class="GbPreferencesPageVim" 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" id="grid1">
+            <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="GtkSpinButton" id="scroll_off_spin">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="valign">center</property>
+                <property name="adjustment">scroll_off_adjustment</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="scroll_off_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" id="label5">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">&lt;b&gt;Scroll Off&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" id="label8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="hexpand">True</property>
+                    <property name="label" translatable="yes">Margin in lines for cursor movement.</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>
+  <object class="GtkAdjustment" id="scroll_off_adjustment">
+    <property name="lower">0</property>
+    <property name="upper">999</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">1</property>
+    <property name="value">0</property>
+  </object>
+</interface>
diff --git a/src/resources/ui/gb-preferences-window.ui b/src/resources/ui/gb-preferences-window.ui
index d2e38d6..1859c80 100644
--- a/src/resources/ui/gb-preferences-window.ui
+++ b/src/resources/ui/gb-preferences-window.ui
@@ -132,6 +132,15 @@
                 <property name="title" translatable="yes">Git</property>
               </packing>
             </child>
+            <child>
+              <object class="GbPreferencesPageVim" id="vim_page">
+                <property name="visible">True</property>
+              </object>
+              <packing>
+                <property name="name">vim</property>
+                <property name="title" translatable="yes">Vim</property>
+              </packing>
+            </child>
           </object>
         </child>
       </object>
diff --git a/src/vim/gb-source-vim.c b/src/vim/gb-source-vim.c
index f17db3d..11631e6 100644
--- a/src/vim/gb-source-vim.c
+++ b/src/vim/gb-source-vim.c
@@ -17,7 +17,6 @@
  */
 
 #define G_LOG_DOMAIN "vim"
-#define SCROLL_OFF 3
 
 #include <errno.h>
 #include <glib/gi18n.h>
@@ -81,7 +80,9 @@ struct _GbSourceVimPrivate
   GtkSourceSearchSettings *search_settings;
   GPtrArray               *captured_events;
   GbSourceVimMode          mode;
+  GSettings               *vim_settings;
   gulong                   key_press_event_handler;
+  gulong                   event_after_handler;
   gulong                   key_release_event_handler;
   gulong                   focus_in_event_handler;
   gulong                   mark_set_handler;
@@ -90,6 +91,7 @@ struct _GbSourceVimPrivate
   guint                    stash_line;
   guint                    stash_line_offset;
   guint                    anim_timeout;
+  guint                    scroll_off;
   gchar                    recording_trigger;
   gchar                    recording_modifier;
   guint                    enabled : 1;
@@ -108,6 +110,20 @@ typedef enum
 
 typedef enum
 {
+  GB_SOURCE_VIM_ALIGNMENT_NONE,
+  GB_SOURCE_VIM_ALIGNMENT_KEEP,
+  GB_SOURCE_VIM_ALIGNMENT_TOP,
+  GB_SOURCE_VIM_ALIGNMENT_BOTTOM
+} GbSourceVimAlignment;
+
+typedef enum
+{
+  GB_SOURCE_VIM_ITER_BOUND_START,
+  GB_SOURCE_VIM_ITER_BOUND_END
+} GbSourceVimIterBound;
+
+typedef enum
+{
   GB_SOURCE_VIM_COMMAND_NOOP,
   GB_SOURCE_VIM_COMMAND_MOVEMENT,
   GB_SOURCE_VIM_COMMAND_CHANGE,
@@ -139,6 +155,12 @@ typedef struct
   GbSourceVimCommandFlags flags;
 } GbSourceVimCommand;
 
+typedef struct
+{
+  gfloat yalign;
+  gint   line;
+} GbSourceVimAdjustedScroll;
+
 typedef enum
 {
   GB_SOURCE_VIM_PHRASE_FAILED,
@@ -209,6 +231,13 @@ static void gb_source_vim_cmd_delete_to_end (GbSourceVim *vim,
 static void gb_source_vim_cmd_insert_before_line (GbSourceVim *vim,
                                                   guint        count,
                                                   gchar        modifier);
+static GbSourceVimAdjustedScroll*
+gb_source_vim_adjust_scroll (GbSourceVim          *vim,
+                             gint                  line,
+                             GbSourceVimAlignment  aligment);
+static void
+gb_source_vim_ensure_scroll_off (GbSourceVim          *vim,
+                                 GbSourceVimIterBound  iter_bound);
 
 GbSourceVim *
 gb_source_vim_new (GtkTextView *text_view)
@@ -545,7 +574,7 @@ gb_source_vim_clear_selection (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 GbSourceVimMode
@@ -778,6 +807,34 @@ gb_source_vim_select_range (GbSourceVim *vim,
 }
 
 static void
+gb_source_vim_ensure_scroll_off (GbSourceVim          *vim,
+                                 GbSourceVimIterBound  iter_bound)
+{
+  GtkTextBuffer *buffer;
+  GtkTextIter iter_end;
+  GtkTextIter iter_start;
+  GtkTextIter iter;
+  guint line;
+  GbSourceVimAdjustedScroll *scroll;
+
+  g_return_if_fail (GB_IS_SOURCE_VIM (vim));
+
+  buffer = gtk_text_view_get_buffer (vim->priv->text_view);
+  gtk_text_buffer_get_selection_bounds (buffer, &iter_start, &iter_end);
+  iter = iter_bound == GB_SOURCE_VIM_ITER_BOUND_START ? iter_start : iter_end;
+  line = gtk_text_iter_get_line (&iter);
+  scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_NONE);
+
+  /* Only adjust scroll if necesary. In this way we avoid
+   * odd jumpings because of yalign imprecision.
+   */
+  if (scroll->yalign >= 0)
+    gtk_text_view_scroll_to_iter (vim->priv->text_view, &iter, 0.0,
+                                  TRUE, 1.0, scroll->yalign);
+  g_free (scroll);
+}
+
+static void
 gb_source_vim_move_line0 (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
@@ -887,7 +944,7 @@ gb_source_vim_move_line_end (GbSourceVim *vim)
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
   insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);;
 }
 
 static void
@@ -1006,7 +1063,6 @@ gb_source_vim_move_backward_word (GbSourceVim *vim)
   GtkTextBuffer *buffer;
   GtkTextIter iter;
   GtkTextIter selection;
-  GtkTextMark *insert;
   gboolean has_selection;
 
   g_assert (GB_IS_SOURCE_VIM (vim));
@@ -1029,8 +1085,7 @@ gb_source_vim_move_backward_word (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -1163,7 +1218,6 @@ static void
 gb_source_vim_move_forward_word (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
@@ -1192,15 +1246,13 @@ gb_source_vim_move_forward_word (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
 gb_source_vim_move_forward_word_end (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
@@ -1225,8 +1277,7 @@ gb_source_vim_move_forward_word_end (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static gboolean
@@ -1248,7 +1299,6 @@ gb_source_vim_move_matching_bracket (GbSourceVim *vim)
 {
   MatchingBracketState state;
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
@@ -1316,8 +1366,7 @@ gb_source_vim_move_matching_bracket (GbSourceVim *vim)
       else
         gtk_text_buffer_select_range (buffer, &iter, &iter);
 
-      insert = gtk_text_buffer_get_insert (buffer);
-      gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+      gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
     }
 }
 
@@ -1372,7 +1421,6 @@ static void
 gb_source_vim_move_forward_paragraph (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter, selection;
   gboolean has_selection;
 
@@ -1403,15 +1451,13 @@ gb_source_vim_move_forward_paragraph (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
 gb_source_vim_move_backward_paragraph (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter, selection;
   gboolean has_selection;
 
@@ -1444,8 +1490,7 @@ gb_source_vim_move_backward_paragraph (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -1453,10 +1498,10 @@ gb_source_vim_move_down (GbSourceVim *vim)
 {
   GbSourceVimPrivate *priv;
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
+  GbSourceVimIterBound iter_bound;
   guint line;
   guint offset;
 
@@ -1468,6 +1513,11 @@ gb_source_vim_move_down (GbSourceVim *vim)
   has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
   line = gtk_text_iter_get_line (&iter);
   offset = vim->priv->target_line_offset;
+  iter_bound = GB_SOURCE_VIM_ITER_BOUND_END;
+
+  /* The cursor is at the start of the selection */
+  if (gtk_text_iter_compare (&iter, &selection) < 0)
+    iter_bound = GB_SOURCE_VIM_ITER_BOUND_START;
 
   /*
    * If we have a whole line selected (from say `V`), then we need to swap
@@ -1528,8 +1578,7 @@ select_to_end:
     }
 
 move_mark:
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, iter_bound);
 }
 
 static void
@@ -1537,10 +1586,10 @@ gb_source_vim_move_up (GbSourceVim *vim)
 {
   GbSourceVimPrivate *priv;
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
+  GbSourceVimIterBound iter_bound;
   guint line;
   guint offset;
 
@@ -1552,10 +1601,21 @@ gb_source_vim_move_up (GbSourceVim *vim)
   has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
   line = gtk_text_iter_get_line (&iter);
   offset = vim->priv->target_line_offset;
+  iter_bound = GB_SOURCE_VIM_ITER_BOUND_START;
 
   if (line == 0)
     return;
 
+  /* The cursor is at the end of the selection */
+  if (gtk_text_iter_compare (&iter, &selection) > 0)
+    iter_bound = GB_SOURCE_VIM_ITER_BOUND_END;
+
+  /*
+   * If we have a whole line selected (from say `V`), then we need to swap
+   * the cursor and selection. This feels to me like a slight bit of a hack.
+   * There may be cause to actually have a selection mode and know the type
+   * of selection (line vs individual characters).
+   */
   if (is_single_line_selection (&iter, &selection))
     {
       if (gtk_text_iter_compare (&iter, &selection) > 0)
@@ -1596,8 +1656,7 @@ gb_source_vim_move_up (GbSourceVim *vim)
     }
 
 move_mark:
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, iter_bound);
 }
 
 static void
@@ -1649,7 +1708,6 @@ static void
 gb_source_vim_delete_selection (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter begin;
   GtkTextIter end;
   GtkClipboard *clipboard;
@@ -1701,8 +1759,7 @@ gb_source_vim_delete_selection (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -1857,7 +1914,6 @@ gb_source_vim_undo (GbSourceVim *vim)
 {
   GtkSourceUndoManager *undo;
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
 
   g_assert (GB_IS_SOURCE_VIM (vim));
@@ -1882,8 +1938,7 @@ gb_source_vim_undo (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -1917,14 +1972,13 @@ gb_source_vim_redo (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
 gb_source_vim_join (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter;
   GtkTextIter selection;
   gboolean has_selection;
@@ -1985,8 +2039,7 @@ gb_source_vim_join (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 
   g_strfreev (parts);
   g_free (slice);
@@ -2031,7 +2084,7 @@ gb_source_vim_insert_nl_before (GbSourceVim *vim)
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -2069,7 +2122,7 @@ gb_source_vim_insert_nl_after (GbSourceVim *vim,
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static void
@@ -2294,7 +2347,6 @@ static void
 gb_source_vim_yank (GbSourceVim *vim)
 {
   GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter begin;
   GtkTextIter end;
   gchar *text;
@@ -2348,8 +2400,7 @@ gb_source_vim_yank (GbSourceVim *vim)
    */
   gtk_text_buffer_select_range (buffer, &begin, &begin);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 }
 
@@ -2533,14 +2584,11 @@ static void
 gb_source_vim_move_to_line_n (GbSourceVim *vim,
                               guint        line)
 {
-  GtkTextBuffer *buffer;
-  GtkTextMark *insert;
   GtkTextIter iter, selection;
   gboolean has_selection;
 
   g_assert (GB_IS_SOURCE_VIM (vim));
 
-  buffer = gtk_text_view_get_buffer (vim->priv->text_view);
   has_selection = gb_source_vim_get_selection_bounds (vim, &iter, &selection);
 
   if (is_single_line_selection (&iter, &selection))
@@ -2565,8 +2613,7 @@ gb_source_vim_move_to_line_n (GbSourceVim *vim,
 
   vim->priv->target_line_offset = gb_source_vim_get_line_offset (vim);
 
-  insert = gtk_text_buffer_get_insert (buffer);
-  gtk_text_view_scroll_mark_onscreen (vim->priv->text_view, insert);
+  gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
 }
 
 static gboolean
@@ -2626,6 +2673,88 @@ gb_source_vim_move_to_iter (GbSourceVim *vim,
                                 TRUE, 0.5, yalign);
 }
 
+static GbSourceVimAdjustedScroll*
+gb_source_vim_adjust_scroll (GbSourceVim          *vim,
+                             gint                  line,
+                             GbSourceVimAlignment  alignment)
+{
+  GdkRectangle rect;
+  gint line_top;
+  gint line_bottom;
+  gint line_current;
+  gint page_lines;
+  GtkTextIter iter_top;
+  GtkTextIter iter_bottom;
+  GtkTextIter iter_current;
+
+  GtkTextBuffer *buffer;
+  gfloat min_yalign, max_yalign, yalign;
+  GbSourceVimAdjustedScroll *result;
+
+  g_assert (GB_IS_SOURCE_VIM (vim));
+
+  gtk_text_view_get_visible_rect (vim->priv->text_view, &rect);
+  gtk_text_view_get_iter_at_location (vim->priv->text_view, &iter_top,
+                                      rect.x, rect.y);
+  gtk_text_view_get_iter_at_location (vim->priv->text_view, &iter_bottom,
+                                      rect.x, rect.y + rect.height);
+
+  buffer = gtk_text_view_get_buffer (vim->priv->text_view);
+  gtk_text_buffer_get_selection_bounds (buffer, &iter_current, NULL);
+
+  result = malloc (sizeof (*result));
+  line_top = gtk_text_iter_get_line (&iter_top);
+  line_bottom = gtk_text_iter_get_line (&iter_bottom);
+  line_current = gtk_text_iter_get_line (&iter_current);
+  page_lines = line_bottom - line_top;
+
+  if (page_lines == 0) {
+    result->line = 0;
+    result->yalign = 0.;
+
+    return result;
+  }
+
+  min_yalign = MIN (vim->priv->scroll_off / (float) page_lines, 0.5);
+  max_yalign = 1.0 - min_yalign;
+  yalign = (line - line_top) / (float) page_lines;
+
+  switch (alignment)
+    {
+    case GB_SOURCE_VIM_ALIGNMENT_NONE:
+      /* Only change yalign if necesary, if not, indicate to the caller that
+       * is not necesary to adjust scroll
+       */
+      if (min_yalign > yalign || yalign > max_yalign)
+       result->yalign = CLAMP (yalign, min_yalign, max_yalign);
+      else
+        result->yalign = -1;
+      result->line = line;
+      break;
+
+    case GB_SOURCE_VIM_ALIGNMENT_KEEP:
+      result->yalign = MAX (0.0, (float)(line_current - line_top) /
+                                (float)(line_bottom - line_top));
+      result->line = line;
+      break;
+
+    case GB_SOURCE_VIM_ALIGNMENT_TOP:
+      result->yalign = CLAMP (0.0, min_yalign, max_yalign);
+      result->line = line + result->yalign * page_lines;
+      break;
+
+    case GB_SOURCE_VIM_ALIGNMENT_BOTTOM:
+      result->yalign = CLAMP (1.0, min_yalign, max_yalign);
+      result->line = line - (1.0 - result->yalign) * page_lines;
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return result;
+}
+
 static void
 gb_source_vim_move_page (GbSourceVim                 *vim,
                          GbSourceVimPageDirectionType direction)
@@ -2635,7 +2764,7 @@ gb_source_vim_move_page (GbSourceVim                 *vim,
   guint offset;
   gint line, line_top, line_bottom, line_current;
   GtkTextBuffer *buffer;
-  gfloat yalign = 0.0;
+  GbSourceVimAdjustedScroll *adjusted_scroll;
 
   g_assert (GB_IS_SOURCE_VIM (vim));
 
@@ -2652,47 +2781,38 @@ gb_source_vim_move_page (GbSourceVim                 *vim,
   line_bottom = gtk_text_iter_get_line (&iter_bottom);
   line_current = gtk_text_iter_get_line (&iter_current);
 
-  if (direction == GB_SOURCE_VIM_HALF_PAGE_UP ||
-      direction == GB_SOURCE_VIM_HALF_PAGE_DOWN)
-    {
-      /* keep current yalign */
-      if (line_bottom != line_top)
-        yalign = MAX (0.0, (float)(line_current - line_top) /
-                           (float)(line_bottom - line_top));
-    }
-
   switch (direction)
     {
     case GB_SOURCE_VIM_HALF_PAGE_UP:
       line = line_current - (line_bottom - line_top) / 2;
+      adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_KEEP);
       break;
     case GB_SOURCE_VIM_HALF_PAGE_DOWN:
       line = line_current + (line_bottom - line_top) / 2;
+      adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_KEEP);
       break;
     case GB_SOURCE_VIM_PAGE_UP:
-      yalign = 1.0;
-      line = gtk_text_iter_get_line (&iter_top) + SCROLL_OFF;
+      line = gtk_text_iter_get_line (&iter_top);
+      adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_BOTTOM);
       break;
     case GB_SOURCE_VIM_PAGE_DOWN:
-      yalign = 0.0;
-      /*
-       * rect.y + rect.height is the next line after the end of the buffer so
-       * now we have to decrease one more.
-       */
-      line = MAX (0, gtk_text_iter_get_line (&iter_bottom) - SCROLL_OFF - 1);
+      line = MAX (0, gtk_text_iter_get_line (&iter_bottom));
+      adjusted_scroll = gb_source_vim_adjust_scroll (vim, line, GB_SOURCE_VIM_ALIGNMENT_TOP);
       break;
     default:
       g_assert_not_reached();
     }
 
-  gtk_text_iter_set_line (&iter_current, line);
+  gtk_text_iter_set_line (&iter_current, adjusted_scroll->line);
 
   for (offset = vim->priv->target_line_offset; offset; offset--)
     if (gtk_text_iter_ends_line (&iter_current) ||
         !gtk_text_iter_forward_char (&iter_current))
       break;
 
-  gb_source_vim_move_to_iter (vim, &iter_current, yalign);
+  gb_source_vim_move_to_iter (vim, &iter_current, adjusted_scroll->yalign);
+
+  g_free (adjusted_scroll);
 }
 
 static void
@@ -3160,6 +3280,15 @@ gb_source_vim_handle_command (GbSourceVim *vim,
   return TRUE;
 }
 
+static void
+gb_source_vim_event_after_cb (GtkTextView *text_view,
+                              GdkEventKey *event,
+                              GbSourceVim *vim)
+{
+  if (vim->priv->mode == GB_SOURCE_VIM_INSERT)
+      gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
+}
+
 static gboolean
 gb_source_vim_key_press_event_cb (GtkTextView *text_view,
                                   GdkEventKey *event,
@@ -3391,6 +3520,13 @@ gb_source_vim_connect (GbSourceVim *vim)
                              vim,
                              0);
 
+  vim->priv->event_after_handler =
+    g_signal_connect_object (vim->priv->text_view,
+                             "event-after",
+                             G_CALLBACK (gb_source_vim_event_after_cb),
+                             vim,
+                             0);
+
   vim->priv->key_release_event_handler =
     g_signal_connect_object (vim->priv->text_view,
                              "key-release-event",
@@ -3443,6 +3579,10 @@ gb_source_vim_disconnect (GbSourceVim *vim)
   vim->priv->key_press_event_handler = 0;
 
   g_signal_handler_disconnect (vim->priv->text_view,
+                               vim->priv->event_after_handler);
+  vim->priv->event_after_handler = 0;
+
+  g_signal_handler_disconnect (vim->priv->text_view,
                                vim->priv->key_release_event_handler);
   vim->priv->key_release_event_handler = 0;
 
@@ -4000,6 +4140,7 @@ gb_source_vim_finalize (GObject *object)
     }
 
   g_clear_object (&priv->search_settings);
+  g_clear_object (&priv->vim_settings);
 
   g_string_free (priv->phrase, TRUE);
   priv->phrase = NULL;
@@ -5297,6 +5438,19 @@ gb_source_vim_class_init (GbSourceVimClass *klass)
 }
 
 static void
+gb_source_vim_scroll_off_changed (GbSourceVim *vim,
+                                  const gchar *key,
+                                  GSettings   *settings)
+{
+  g_return_if_fail (GB_IS_SOURCE_VIM (vim));
+  g_return_if_fail (G_IS_SETTINGS (settings));
+
+  vim->priv->scroll_off = g_settings_get_int (settings, "scroll-off");
+  if (vim->priv->text_view != NULL)
+       gb_source_vim_ensure_scroll_off (vim, GB_SOURCE_VIM_ITER_BOUND_START);
+}
+
+static void
 gb_source_vim_init (GbSourceVim *vim)
 {
   vim->priv = gb_source_vim_get_instance_private (vim);
@@ -5306,6 +5460,14 @@ gb_source_vim_init (GbSourceVim *vim)
   vim->priv->search_settings = gtk_source_search_settings_new ();
   vim->priv->captured_events =
     g_ptr_array_new_with_free_func ((GDestroyNotify)gdk_event_free);
+
+  vim->priv->vim_settings = g_settings_new ("org.gnome.builder.editor.vim");
+  g_signal_connect_object (vim->priv->vim_settings,
+                           "changed::scroll-off",
+                           G_CALLBACK (gb_source_vim_scroll_off_changed),
+                           vim,
+                           G_CONNECT_SWAPPED);
+  gb_source_vim_scroll_off_changed (vim, NULL, vim->priv->vim_settings);
 }
 
 GType



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