[gtksourceview/wip/chergert/vim: 11/363] start on base state implementation




commit fdd9ca73110543f9123bdf7a5a8055e287efeb21
Author: Christian Hergert <chergert redhat com>
Date:   Thu Oct 21 14:12:36 2021 -0700

    start on base state implementation

 gtksourceview/vim/gtk-source-vim-state.c | 408 +++++++++++++++++++++++++++++++
 gtksourceview/vim/gtk-source-vim-state.h |   7 +-
 2 files changed, 414 insertions(+), 1 deletion(-)
---
diff --git a/gtksourceview/vim/gtk-source-vim-state.c b/gtksourceview/vim/gtk-source-vim-state.c
new file mode 100644
index 00000000..31fb2843
--- /dev/null
+++ b/gtksourceview/vim/gtk-source-vim-state.c
@@ -0,0 +1,408 @@
+/*
+ * This file is part of GtkSourceView
+ *
+ * Copyright 2021 Christian Hergert <chergert redhat com>
+ *
+ * GtkSourceView is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GtkSourceView 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include "gtk-source-vim-state.h"
+
+typedef struct
+{
+       GtkSourceVimState *parent;
+       GtkSourceVimState *child;
+       GtkSourceView     *view;
+} GtkSourceVimStatePrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GtkSourceVimState, gtk_source_vim_state, G_TYPE_OBJECT)
+
+enum {
+       PROP_0,
+       PROP_PARENT,
+       PROP_VIEW,
+       N_PROPS
+};
+
+static GParamSpec *properties [N_PROPS];
+
+static inline void
+get_key_string (guint           keyval,
+                GdkModifierType mods,
+                char            str[16])
+{
+       int pos = 0;
+
+       if (keyval && (mods & GDK_CONTROL_MASK) != 0)
+       {
+               str[pos++] = '^';
+       }
+
+       switch (keyval)
+       {
+               case GDK_KEY_Escape:
+                       str[pos++] = '[';
+                       break;
+
+               case GDK_KEY_ISO_Left_Tab:
+               case GDK_KEY_Tab:
+                       str[pos++] = '\t';
+                       break;
+
+               case GDK_KEY_Return:
+               case GDK_KEY_KP_Enter:
+                       str[pos++] = '\n';
+                       break;
+
+               case GDK_KEY_Control_R:
+               case GDK_KEY_Control_L:
+                       pos = 0;
+                       break;
+
+               default:
+               {
+                       gunichar ch = gdk_keyval_to_unicode (keyval);
+                       pos += g_unichar_to_utf8 (ch, &str[pos]);
+                       break;
+               }
+       }
+
+       str[pos] = 0;
+}
+
+static gboolean
+gtk_source_vim_state_real_handle_event (GtkSourceVimState *self,
+                                        GdkEvent          *event)
+{
+       guint keyval;
+       guint keycode;
+       GdkModifierType mods;
+       char string[16];
+
+       g_assert (GTK_SOURCE_IS_VIM_STATE (self));
+       g_assert (event != NULL);
+
+       if (gdk_event_get_event_type (event) != GDK_KEY_PRESS)
+       {
+               return FALSE;
+       }
+
+       if (GTK_SOURCE_VIM_STATE_GET_CLASS (self)->handle_keypress == NULL)
+       {
+               return FALSE;
+       }
+
+       keyval = gdk_key_event_get_keyval (event);
+       keycode = gdk_key_event_get_keycode (event);
+       mods = gdk_key_event_get_consumed_modifiers (event);
+       keyval_to_string (keyval, mods, string);
+
+       return GTK_SOURCE_VIM_STATE_GET_CLASS (self)->handle_keypress (self, keyval, keycode, mods, string);
+}
+
+static void
+gtk_source_vim_state_dispose (GObject *object)
+{
+       GtkSourceVimState *self = (GtkSourceVimState *)object;
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       /* This is extra protection to ensure that we don't have a dangling
+        * child pointer in the parent state. Generally, if
+        * gtk_source_vim_state_pop() is called properly this cannot happen
+        * as it will clear the child pointer. At that point only some
+        * states will choose to hold on to the child (such as Normal mode
+        * to allow for repeating the last command).
+        */
+       if (priv->parent != NULL)
+       {
+               GtkSourceVimStatePrivate *parent_priv = gtk_source_vim_state_get_instance_private 
(priv->parent);
+
+               if (parent_priv->child == self)
+               {
+                       parent_priv->child = NULL;
+               }
+       }
+
+       g_clear_weak_pointer (&priv->view);
+       g_clear_object (&priv->parent);
+
+       G_OBJECT_CLASS (gtk_source_vim_state_parent_class)->dispose (object);
+}
+
+static void
+gtk_source_vim_state_get_property (GObject    *object,
+                                   guint       prop_id,
+                                   GValue     *value,
+                                   GParamSpec *pspec)
+{
+       GtkSourceVimState *self = GTK_SOURCE_VIM_STATE (object);
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       switch (prop_id)
+       {
+               case PROP_PARENT:
+                       g_value_set_object (value, priv->parent);
+                       break;
+
+               case PROP_VIEW:
+                       g_value_set_object (value, priv->view);
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_vim_state_set_property (GObject      *object,
+                                   guint         prop_id,
+                                   const GValue *value,
+                                   GParamSpec   *pspec)
+{
+       GtkSourceVimState *self = GTK_SOURCE_VIM_STATE (object);
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       switch (prop_id)
+       {
+               case PROP_PARENT:
+                       g_set_object (&priv->parent, g_value_get_object (value));
+                       break;
+
+               case PROP_VIEW:
+                       g_set_weak_pointer (&priv->view, g_value_get_object (value));
+                       break;
+
+               default:
+                       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+       }
+}
+
+static void
+gtk_source_vim_state_class_init (GtkSourceVimStateClass *klass)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+       object_class->dispose = gtk_source_vim_state_dispose;
+       object_class->get_property = gtk_source_vim_state_get_property;
+       object_class->set_property = gtk_source_vim_state_set_property;
+
+       klass->handle_event = gtk_source_vim_state_real_handle_event;
+
+       properties [PROP_PARENT] =
+               g_param_spec_object ("parent",
+                                    "Parent",
+                                    "The parent state",
+                                    GTK_SOURCE_TYPE_VIM_STATE,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       properties [PROP_VIEW] =
+               g_param_spec_object ("view",
+                                    "View",
+                                    "The source view",
+                                    GTK_SOURCE_TYPE_VIEW,
+                                    (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gtk_source_vim_state_init (GtkSourceVimState *self)
+{
+}
+
+GtkSourceView *
+gtk_source_vim_state_get_view (GtkSourceVimState *self)
+{
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), NULL);
+
+       if (priv->view)
+               return priv->view;
+
+       if (priv->parent)
+               return gtk_source_vim_state_get_view (priv->parent);
+
+       return NULL;
+}
+
+GtkSourceBuffer *
+gtk_source_vim_state_get_buffer (GtkSourceVimState *self,
+                                 GtkTextIter       *insert,
+                                 GtkTextIter       *selection_bound)
+{
+       GtkSourceView *view;
+       GtkTextBuffer *buffer;
+
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), NULL);
+
+       if (!(view = gtk_source_vim_state_get_view (self)))
+               return NULL;
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+       g_assert (GTK_SOURCE_IS_BUFFER (buffer));
+
+       if (insert != NULL)
+       {
+               gtk_text_buffer_get_iter_at_mark (buffer, insert, gtk_text_buffer_get_insert (buffer));
+       }
+
+       if (selection_bound != NULL)
+       {
+               gtk_text_buffer_get_iter_at_mark (buffer, insert, gtk_text_buffer_get_selection_bound 
(buffer));
+       }
+
+       return GTK_SOURCE_BUFFER (buffer);
+}
+
+void
+gtk_source_vim_state_beep (GtkSourceVimState *self)
+{
+       GtkSourceView *view;
+       GdkDisplay *display;
+
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (self));
+
+       if ((view = gtk_source_vim_state_get_view (self)) &&
+           (display = gtk_widget_get_display (GTK_WIDGET (view))))
+       {
+               gdk_display_beep (display);
+       }
+}
+
+GtkSourceVimState *
+gtk_source_vim_state_get_parent (GtkSourceVimState *self)
+{
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), NULL);
+
+       return priv->parent;
+}
+
+GtkSourceVimState *
+gtk_source_vim_state_get_root (GtkSourceVimState *self)
+{
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), NULL);
+
+       if (priv->parent == NULL)
+               return self;
+
+       return gtk_source_vim_state_get_root (priv->parent);
+}
+
+void
+gtk_source_vim_state_repeat (GtkSourceVimState *self,
+                             int                repeat)
+{
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (self));
+
+       if (GTK_SOURCE_VIM_GET_CLASS (self)->repeat)
+       {
+               GTK_SOURCE_VIM_GET_CLASS (self)->repeat (self, repeat);
+       }
+}
+
+gboolean
+gtk_source_vim_get_can_repeat (GtkSourceVimState *self)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), FALSE);
+
+       if (GTK_SOURCE_VIM_GET_CLASS (self)->get_can_repeat)
+       {
+               return GTK_SOURCE_VIM_GET_CLASS (self)->get_can_repeat (self);
+       }
+
+       return FALSE;
+}
+
+gboolean
+gtk_source_vim_state_handle_event (GtkSourceVimState *self,
+                                   GdkEvent          *event)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_VIM_STATE (self), FALSE);
+       g_return_val_if_fail (event != NULL, FALSE);
+
+       if (GTK_SOURCE_VIM_GET_CLASS (self)->handle_event)
+       {
+               return GTK_SOURCE_VIM_GET_CLASS (self)->handle_event (self, event);
+       }
+
+       return FALSE;
+}
+
+void
+gtk_source_vim_state_push (GtkSourceVimState *self,
+                           GtkSourceVimState *new_state)
+{
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (self));
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (new_state));
+       g_return_if_fail (gtk_source_vim_state_get_parent (new_state) == NULL);
+
+       /* Associate the state with our view and set @self as parent to
+        * hold the ref back to @self. We will not hold a reference to
+        * @new_state to avoid a circular reference.
+        */
+       g_object_set (new_state,
+                     "parent", self,
+                     "view", priv->view,
+                     NULL);
+
+       /* Keep a pointer to the child. When the child is disposed, it will
+        * clear our priv->child field if it still matches @new_state.
+        */
+       priv->child = new_state;
+
+       if (GTK_SOURCE_VIM_STATE_GET_CLASS (self)->suspend)
+       {
+               GTK_SOURCE_VIM_STATE_GET_CLASS (self)->suspend (self, new_state);
+       }
+
+       if (GTK_SOURCE_VIM_STATE_GET_CLASS (new_state)->enter)
+       {
+               GTK_SOURCE_VIM_STATE_GET_CLASS (self)->enter (new_state);
+       }
+}
+
+void
+gtk_source_vim_state_pop (GtkSourceVimState *self)
+{
+       GtkSourceVimStatePrivate *priv = gtk_source_vim_state_get_instance_private (self);
+       g_autoptr(GtkSourceVimState) child = NULL;
+
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (self));
+       g_return_if_fail (GTK_SOURCE_IS_VIM_STATE (priv->child));
+
+       g_set_object (&child, priv->child);
+       priv->child = NULL;
+
+       if (GTK_SOURCE_VIM_STATE_GET_CLASS (child)->leave)
+       {
+               GTK_SOURCE_VIM_STATE_GET_CLASS (self)->leave (child);
+       }
+
+       if (GTK_SOURCE_VIM_STATE_GET_CLASS (self)->restore)
+       {
+               GTK_SOURCE_VIM_STATE_GET_CLASS (self)->restore (self, child);
+       }
+}
diff --git a/gtksourceview/vim/gtk-source-vim-state.h b/gtksourceview/vim/gtk-source-vim-state.h
index 4f21a305..89a61c7f 100644
--- a/gtksourceview/vim/gtk-source-vim-state.h
+++ b/gtksourceview/vim/gtk-source-vim-state.h
@@ -54,9 +54,14 @@ struct _GtkSourceVimStateClass
                                     int                repeat);
 };
 
+void               gtk_source_vim_state_push           (GtkSourceVimState *self,
+                                                        GtkSourceVimState *new_state);
+void               gtk_source_vim_state_pop            (GtkSourceVimState *self);
 void               gtk_source_vim_state_beep           (GtkSourceVimState *self);
 GtkSourceView     *gtk_source_vim_state_get_view       (GtkSourceVimState *self);
-GtkSourceBuffer   *gtk_source_vim_state_get_buffer     (GtkSourceVimState *self);
+GtkSourceBuffer   *gtk_source_vim_state_get_buffer     (GtkSourceVimState *self,
+                                                        GtkTextIter       *insert,
+                                                        GtkTextIter       *selection_bound);
 GtkSourceVimState *gtk_source_vim_state_get_root       (GtkSourceVimState *self);
 GtkSourceVimState *gtk_source_vim_state_get_parent     (GtkSourceVimState *self);
 gboolean           gtk_source_vim_state_handle_event   (GtkSourceVimState *self,


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