[gnome-builder] plugins/vim: port to GTK 4



commit 21e30bfa4d53d4d2bc6c89f3ea80a92131aed0be
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jul 11 23:30:10 2022 -0700

    plugins/vim: port to GTK 4
    
    This uses the new Vim emulation C API in GtkSourceView which I added in
    GtkSourceView 5.4. The widgets are integrated with the workspace from
    the workspace addin. A vim input method is configured when enabled.

 src/plugins/vim/gb-vim.c                           | 1683 -----------
 src/plugins/vim/gb-vim.h                           |   48 -
 src/plugins/vim/gbp-vim-command-provider.c         |  121 -
 src/plugins/vim/gbp-vim-command.c                  |  154 -
 src/plugins/vim/gbp-vim-editor-page-addin.c        |  449 +++
 ...mand-provider.h => gbp-vim-editor-page-addin.h} |    8 +-
 src/plugins/vim/gbp-vim-preferences-addin.c        |   45 +-
 src/plugins/vim/gbp-vim-preferences-addin.h        |    2 +-
 src/plugins/vim/gbp-vim-workspace-addin.c          |  246 ++
 ...gbp-vim-command.h => gbp-vim-workspace-addin.h} |   19 +-
 src/plugins/vim/keybindings/vim.css                | 3099 --------------------
 src/plugins/vim/meson.build                        |    5 +-
 src/plugins/vim/vim-plugin.c                       |   12 +-
 src/plugins/vim/vim.gresource.xml                  |    1 -
 src/plugins/vim/vim.plugin                         |    6 +-
 15 files changed, 748 insertions(+), 5150 deletions(-)
---
diff --git a/src/plugins/vim/gbp-vim-editor-page-addin.c b/src/plugins/vim/gbp-vim-editor-page-addin.c
new file mode 100644
index 000000000..8cb1f85c2
--- /dev/null
+++ b/src/plugins/vim/gbp-vim-editor-page-addin.c
@@ -0,0 +1,449 @@
+/* gbp-vim-editor-page-addin.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vim-editor-page-addin"
+
+#include "config.h"
+
+#include <libide-gui.h>
+#include <libide-editor.h>
+
+#include "gbp-vim-editor-page-addin.h"
+#include "gbp-vim-workspace-addin.h"
+
+static void     gbp_vim_editor_page_addin_load                       (IdeEditorPageAddin    *addin,
+                                                                      IdeEditorPage         *page);
+static void     gbp_vim_editor_page_addin_unload                     (IdeEditorPageAddin    *addin,
+                                                                      IdeEditorPage         *page);
+static void     gbp_vim_editor_page_addin_notify_active_cb           (GbpVimEditorPageAddin *self,
+                                                                      GParamSpec            *pspec,
+                                                                      GbpVimWorkspaceAddin  
*workspace_addin);
+static gboolean gbp_vim_editor_page_addin_execute_command_cb         (GbpVimEditorPageAddin *self,
+                                                                      const char            *command,
+                                                                      GtkSourceVimIMContext *im_context);
+static void     gbp_vim_editor_page_addin_notify_command_bar_text_cb (GbpVimEditorPageAddin *self,
+                                                                      GParamSpec            *pspec,
+                                                                      GtkSourceVimIMContext *im_context);
+static void     gbp_vim_editor_page_addin_notify_command_text_cb     (GbpVimEditorPageAddin *self,
+                                                                      GParamSpec            *pspec,
+                                                                      GtkSourceVimIMContext *im_context);
+static void     gbp_vim_editor_page_addin_update                     (GbpVimEditorPageAddin *self);
+
+struct _GbpVimEditorPageAddin
+{
+  GObject               parent_instance;
+
+  /* Unowned pointers */
+  IdeEditorPage        *page;
+  GbpVimWorkspaceAddin *workspace_addin;
+
+  /* Owned references */
+  GtkEventController   *key_controller;
+
+  guint                 enabled : 1;
+};
+
+static void
+editor_page_addin_iface_init (IdeEditorPageAddinInterface *iface)
+{
+  iface->load = gbp_vim_editor_page_addin_load;
+  iface->unload = gbp_vim_editor_page_addin_unload;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpVimEditorPageAddin, gbp_vim_editor_page_addin, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_EDITOR_PAGE_ADDIN, 
editor_page_addin_iface_init))
+
+static void
+gbp_vim_editor_page_addin_class_init (GbpVimEditorPageAddinClass *klass)
+{
+}
+
+static void
+gbp_vim_editor_page_addin_init (GbpVimEditorPageAddin *self)
+{
+}
+
+static void
+gbp_vim_editor_page_addin_update (GbpVimEditorPageAddin *self)
+{
+  IdeWorkspaceAddin *workspace_addin;
+  IdeSourceView *view;
+  IdeWorkspace *workspace;
+  GtkIMContext *im_context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_assert (IDE_IS_EDITOR_PAGE (self->page));
+
+  view = ide_editor_page_get_view (self->page);
+  im_context = gtk_event_controller_key_get_im_context (GTK_EVENT_CONTROLLER_KEY (self->key_controller));
+
+  if (self->workspace_addin != NULL)
+    {
+      g_signal_handlers_disconnect_by_func (self->workspace_addin,
+                                            G_CALLBACK (gbp_vim_editor_page_addin_notify_active_cb),
+                                            self);
+      self->workspace_addin = NULL;
+    }
+
+  if (!(workspace = ide_widget_get_workspace (GTK_WIDGET (self->page))) ||
+      !(workspace_addin = ide_workspace_addin_find_by_module_name (workspace, "vim")) ||
+      !GBP_IS_VIM_WORKSPACE_ADDIN (workspace_addin))
+    IDE_GOTO (disabled);
+
+  self->workspace_addin = GBP_VIM_WORKSPACE_ADDIN (workspace_addin);
+
+  g_signal_connect_object (self->workspace_addin,
+                           "notify::active",
+                           G_CALLBACK (gbp_vim_editor_page_addin_notify_active_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  if (!gbp_vim_workspace_addin_get_active (self->workspace_addin))
+    IDE_GOTO (disabled);
+
+  if (!self->enabled)
+    {
+      self->enabled = TRUE;
+      gtk_im_context_set_client_widget (im_context, GTK_WIDGET (view));
+      gtk_widget_add_controller (GTK_WIDGET (view), g_object_ref (self->key_controller));
+    }
+
+  IDE_EXIT;
+
+disabled:
+  if (self->enabled)
+    {
+      self->enabled = FALSE;
+      gtk_im_context_set_client_widget (im_context, NULL);
+      gtk_widget_remove_controller (GTK_WIDGET (view), self->key_controller);
+      gtk_event_controller_reset (self->key_controller);
+    }
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_notify_active_cb (GbpVimEditorPageAddin *self,
+                                            GParamSpec            *pspec,
+                                            GbpVimWorkspaceAddin  *workspace_addin)
+{
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+  g_assert (GBP_IS_VIM_WORKSPACE_ADDIN (workspace_addin));
+
+  gbp_vim_editor_page_addin_update (self);
+}
+
+static void
+gbp_vim_editor_page_addin_notify_command_bar_text_cb (GbpVimEditorPageAddin *self,
+                                                      GParamSpec            *pspec,
+                                                      GtkSourceVimIMContext *im_context)
+{
+  const char *command_bar;
+
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+  g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+  command_bar = gtk_source_vim_im_context_get_command_bar_text (im_context);
+
+  if (self->workspace_addin)
+    gbp_vim_workspace_addin_set_command_bar (self->workspace_addin, command_bar);
+}
+
+static void
+gbp_vim_editor_page_addin_notify_command_text_cb (GbpVimEditorPageAddin *self,
+                                                  GParamSpec            *pspec,
+                                                  GtkSourceVimIMContext *im_context)
+{
+  const char *command;
+
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+  g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+
+  command = gtk_source_vim_im_context_get_command_text (im_context);
+
+  if (self->workspace_addin)
+    gbp_vim_workspace_addin_set_command (self->workspace_addin, command);
+}
+
+static void
+gbp_vim_editor_page_addin_load (IdeEditorPageAddin *addin,
+                                IdeEditorPage      *page)
+{
+  GbpVimEditorPageAddin *self = (GbpVimEditorPageAddin *)addin;
+  GtkIMContext *im_context;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+
+  self->page = page;
+
+  self->key_controller = gtk_event_controller_key_new ();
+  im_context = gtk_source_vim_im_context_new ();
+  gtk_event_controller_set_propagation_phase (self->key_controller, GTK_PHASE_CAPTURE);
+  gtk_event_controller_key_set_im_context (GTK_EVENT_CONTROLLER_KEY (self->key_controller), im_context);
+
+  g_signal_connect_object (im_context,
+                           "notify::command-bar-text",
+                           G_CALLBACK (gbp_vim_editor_page_addin_notify_command_bar_text_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (im_context,
+                           "notify::command-text",
+                           G_CALLBACK (gbp_vim_editor_page_addin_notify_command_text_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (im_context,
+                           "execute-command",
+                           G_CALLBACK (gbp_vim_editor_page_addin_execute_command_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  g_object_unref (im_context);
+
+  gbp_vim_editor_page_addin_update (self);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_unload (IdeEditorPageAddin *addin,
+                                  IdeEditorPage      *page)
+{
+  GbpVimEditorPageAddin *self = (GbpVimEditorPageAddin *)addin;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE_ADDIN (self));
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+
+  if (self->enabled)
+    {
+      IdeSourceView *view = ide_editor_page_get_view (page);
+
+      self->enabled = FALSE;
+      gtk_widget_remove_controller (GTK_WIDGET (view), self->key_controller);
+    }
+
+  g_clear_object (&self->key_controller);
+
+  self->page = NULL;
+  self->workspace_addin = NULL;
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_save_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  IdeEditorPage *page = (IdeEditorPage *)object;
+  g_autoptr(GbpVimEditorPageAddin) self = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+
+  if (!ide_editor_page_save_finish (page, result, &error))
+    g_warning ("Failed to save page: %s", error->message);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_save_and_close_cb (GObject      *object,
+                                             GAsyncResult *result,
+                                             gpointer      user_data)
+{
+  IdeEditorPage *page = (IdeEditorPage *)object;
+  g_autoptr(GbpVimEditorPageAddin) self = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+
+  if (!ide_editor_page_save_finish (page, result, &error))
+    g_warning ("Failed to save page: %s", error->message);
+  else if (self->page != NULL)
+    panel_widget_close (PANEL_WIDGET (self->page));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_discard_cb (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  IdeEditorPage *page = (IdeEditorPage *)object;
+  g_autoptr(GbpVimEditorPageAddin) self = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+
+  if (!ide_editor_page_discard_changes_finish (page, result, &error))
+    g_warning ("Failed to discard changes: %s", error->message);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_editor_page_addin_discard_and_close_cb (GObject      *object,
+                                                GAsyncResult *result,
+                                                gpointer      user_data)
+{
+  IdeEditorPage *page = (IdeEditorPage *)object;
+  g_autoptr(GbpVimEditorPageAddin) self = user_data;
+  g_autoptr(GError) error = NULL;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_EDITOR_PAGE (page));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+
+  if (!ide_editor_page_discard_changes_finish (page, result, &error))
+    g_warning ("Failed to discard changes: %s", error->message);
+  else if (self->page != NULL)
+    panel_widget_close (PANEL_WIDGET (self->page));
+
+  IDE_EXIT;
+}
+
+static gboolean
+gbp_vim_editor_page_addin_execute_command_cb (GbpVimEditorPageAddin *self,
+                                              const char            *command,
+                                              GtkSourceVimIMContext *im_context)
+{
+  IdeBuffer *buffer;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_VIM_EDITOR_PAGE_ADDIN (self));
+  g_assert (GTK_SOURCE_IS_VIM_IM_CONTEXT (im_context));
+  g_assert (IDE_IS_EDITOR_PAGE (self->page));
+
+  g_debug ("Request to execute command %s", command);
+
+  buffer = ide_editor_page_get_buffer (self->page);
+
+  if (g_str_equal (command, ":q") ||
+      g_str_equal (command, ":quit") ||
+      g_str_equal (command, "^Wc"))
+    {
+      IdeGrid *grid = IDE_GRID (gtk_widget_get_ancestor (GTK_WIDGET (self->page), IDE_TYPE_GRID));
+      PanelGridColumn *column = PANEL_GRID_COLUMN (gtk_widget_get_ancestor (GTK_WIDGET (self->page), 
PANEL_TYPE_GRID_COLUMN));
+      IdeFrame *frame = IDE_FRAME (gtk_widget_get_ancestor (GTK_WIDGET (self->page), IDE_TYPE_FRAME));
+
+      panel_widget_close (PANEL_WIDGET (self->page));
+
+      if (panel_frame_get_empty (PANEL_FRAME (frame)) &&
+          (panel_grid_get_n_columns (PANEL_GRID (grid)) > 1 ||
+           panel_grid_column_get_n_rows (column) > 1))
+        gtk_widget_activate_action (GTK_WIDGET (frame), "frame.close", NULL);
+
+      IDE_RETURN (TRUE);
+    }
+
+  if (g_str_equal (command, ":q!") || g_str_equal (command, ":quit!"))
+    {
+      ide_editor_page_discard_changes_async (self->page,
+                                             NULL,
+                                             gbp_vim_editor_page_addin_discard_and_close_cb,
+                                             g_object_ref (self));
+      IDE_RETURN (TRUE);
+    }
+
+  if (g_str_equal (command, ":w") || g_str_equal (command, ":write"))
+    {
+      ide_editor_page_save_async (self->page,
+                                  NULL,
+                                  gbp_vim_editor_page_addin_save_cb,
+                                  g_object_ref (self));
+      IDE_RETURN (TRUE);
+    }
+
+  if (g_str_equal (command, ":wq"))
+    {
+      ide_editor_page_save_async (self->page,
+                                  NULL,
+                                  gbp_vim_editor_page_addin_save_and_close_cb,
+                                  g_object_ref (self));
+      IDE_RETURN (TRUE);
+    }
+
+  if (g_str_equal (command, ":e!") ||
+      (g_str_equal (command, ":e") &&
+       !gtk_text_buffer_get_modified (GTK_TEXT_BUFFER (buffer))))
+    {
+      ide_editor_page_discard_changes_async (self->page,
+                                             NULL,
+                                             gbp_vim_editor_page_addin_discard_cb,
+                                             g_object_ref (self));
+      IDE_RETURN (TRUE);
+    }
+
+  if (g_str_equal (command, "^Wv"))
+    {
+      g_autoptr(IdePanelPosition) position = ide_page_get_position (IDE_PAGE (self->page));
+      IdePage *new_page = ide_page_create_split (IDE_PAGE (self->page));
+      IdeWorkspace *workspace = ide_widget_get_workspace (GTK_WIDGET (self->page));
+      guint column = 0;
+
+      ide_panel_position_get_column (position, &column);
+      ide_panel_position_set_column (position, column+1);
+      ide_panel_position_set_row (position, 0);
+
+      ide_workspace_add_grid_column (workspace, column+1);
+      ide_workspace_add_page (workspace, new_page, position);
+    }
+
+  if (g_str_equal (command, "^Ws") ||
+      g_str_equal (command, ":split"))
+    {
+      g_autoptr(IdePanelPosition) position = ide_page_get_position (IDE_PAGE (self->page));
+      IdePage *new_page = ide_page_create_split (IDE_PAGE (self->page));
+      IdeWorkspace *workspace = ide_widget_get_workspace (GTK_WIDGET (self->page));
+      guint row = 0;
+
+      ide_panel_position_get_row (position, &row);
+      ide_panel_position_set_row (position, row+1);
+
+      ide_workspace_add_page (workspace, new_page, position);
+    }
+
+  IDE_RETURN (FALSE);
+}
diff --git a/src/plugins/vim/gbp-vim-command-provider.h b/src/plugins/vim/gbp-vim-editor-page-addin.h
similarity index 73%
rename from src/plugins/vim/gbp-vim-command-provider.h
rename to src/plugins/vim/gbp-vim-editor-page-addin.h
index 25a4bc03e..d7edc9b78 100644
--- a/src/plugins/vim/gbp-vim-command-provider.h
+++ b/src/plugins/vim/gbp-vim-editor-page-addin.h
@@ -1,6 +1,6 @@
-/* gbp-vim-command-provider.h
+/* gbp-vim-editor-page-addin.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat 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
@@ -24,8 +24,8 @@
 
 G_BEGIN_DECLS
 
-#define GBP_TYPE_VIM_COMMAND_PROVIDER (gbp_vim_command_provider_get_type())
+#define GBP_TYPE_VIM_EDITOR_PAGE_ADDIN (gbp_vim_editor_page_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpVimCommandProvider, gbp_vim_command_provider, GBP, VIM_COMMAND_PROVIDER, GObject)
+G_DECLARE_FINAL_TYPE (GbpVimEditorPageAddin, gbp_vim_editor_page_addin, GBP, VIM_EDITOR_PAGE_ADDIN, GObject)
 
 G_END_DECLS
diff --git a/src/plugins/vim/gbp-vim-preferences-addin.c b/src/plugins/vim/gbp-vim-preferences-addin.c
index b29f090d8..ce99b63ed 100644
--- a/src/plugins/vim/gbp-vim-preferences-addin.c
+++ b/src/plugins/vim/gbp-vim-preferences-addin.c
@@ -23,6 +23,7 @@
 #include "config.h"
 
 #include <glib/gi18n.h>
+
 #include <libide-gui.h>
 
 #include "gbp-vim-preferences-addin.h"
@@ -30,41 +31,42 @@
 struct _GbpVimPreferencesAddin
 {
   GObject parent_instance;
-  guint   keybinding_id;
+};
+
+static const IdePreferenceItemEntry items[] = {
+  { "keyboard", "keybindings", "vim", 0, ide_preferences_window_check,
+    N_("Vim"),
+    N_("Emulate keyboard shortcuts from Vim"),
+    "org.gnome.builder.editor", NULL, "keybindings", "'vim'" },
 };
 
 static void
-gbp_vim_preferences_addin_load (IdePreferencesAddin *addin,
-                                DzlPreferences      *preferences)
+gbp_vim_preferences_addin_load (IdePreferencesAddin  *addin,
+                                IdePreferencesWindow *window,
+                                IdeContext           *context)
 {
   GbpVimPreferencesAddin *self = (GbpVimPreferencesAddin *)addin;
 
+  IDE_ENTRY;
+
   g_assert (GBP_IS_VIM_PREFERENCES_ADDIN (self));
-  g_assert (DZL_IS_PREFERENCES (preferences));
-
-  self->keybinding_id = dzl_preferences_add_radio (preferences,
-                                                   "keyboard",
-                                                   "mode",
-                                                   "org.gnome.builder.editor",
-                                                   "keybindings",
-                                                   NULL,
-                                                   "\"vim\"",
-                                                   _("Vim"),
-                                                   _("Emulates the Vim text editor"),
-                                                   NULL,
-                                                   30);
+  g_assert (IDE_IS_PREFERENCES_WINDOW (window));
+
+  ide_preferences_window_add_items (window, items, G_N_ELEMENTS (items), window, NULL);
+
+  IDE_EXIT;
 }
 
 static void
-gbp_vim_preferences_addin_unload (IdePreferencesAddin *addin,
-                                  DzlPreferences      *preferences)
+gbp_vim_preferences_addin_unload (IdePreferencesAddin  *addin,
+                                  IdePreferencesWindow *window,
+                                  IdeContext           *context)
 {
   GbpVimPreferencesAddin *self = (GbpVimPreferencesAddin *)addin;
 
   g_assert (GBP_IS_VIM_PREFERENCES_ADDIN (self));
-  g_assert (DZL_IS_PREFERENCES (preferences));
+  g_assert (IDE_IS_PREFERENCES_WINDOW (window));
 
-  dzl_preferences_remove_id (preferences, self->keybinding_id);
 }
 
 static void
@@ -75,8 +77,7 @@ preferences_addin_iface_init (IdePreferencesAddinInterface *iface)
 }
 
 G_DEFINE_FINAL_TYPE_WITH_CODE (GbpVimPreferencesAddin, gbp_vim_preferences_addin, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN,
-                                                preferences_addin_iface_init))
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_PREFERENCES_ADDIN, 
preferences_addin_iface_init))
 
 static void
 gbp_vim_preferences_addin_class_init (GbpVimPreferencesAddinClass *klass)
diff --git a/src/plugins/vim/gbp-vim-preferences-addin.h b/src/plugins/vim/gbp-vim-preferences-addin.h
index 7a8663cf4..d20fda95f 100644
--- a/src/plugins/vim/gbp-vim-preferences-addin.h
+++ b/src/plugins/vim/gbp-vim-preferences-addin.h
@@ -1,6 +1,6 @@
 /* gbp-vim-preferences-addin.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2018-2022 Christian Hergert <chergert redhat 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
diff --git a/src/plugins/vim/gbp-vim-workspace-addin.c b/src/plugins/vim/gbp-vim-workspace-addin.c
new file mode 100644
index 000000000..c7579c191
--- /dev/null
+++ b/src/plugins/vim/gbp-vim-workspace-addin.c
@@ -0,0 +1,246 @@
+/* gbp-vim-workspace-addin.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vim-workspace-addin"
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+#include <libpanel.h>
+
+#include <libide-gui.h>
+
+#include "gbp-vim-workspace-addin.h"
+
+struct _GbpVimWorkspaceAddin
+{
+  GObject parent_instance;
+
+  GSettings *editor_settings;
+
+  GtkLabel *command_bar;
+  GtkLabel *command;
+
+  guint active : 1;
+};
+
+enum {
+  PROP_0,
+  PROP_ACTIVE,
+  N_PROPS
+};
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+on_keybindings_changed_cb (GbpVimWorkspaceAddin *self,
+                           const char           *key,
+                           GSettings            *editor_settings)
+{
+  g_autofree char *keybindings = NULL;
+  gboolean active;
+
+  g_assert (GBP_IS_VIM_WORKSPACE_ADDIN (self));
+  g_assert (g_strcmp0 (key, "keybindings") == 0);
+  g_assert (G_IS_SETTINGS (editor_settings));
+
+  keybindings = g_settings_get_string (editor_settings, "keybindings");
+  active = g_strcmp0 (keybindings, "vim") == 0;
+
+  if (active != self->active)
+    {
+      self->active = active;
+      g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIVE]);
+    }
+}
+
+static void
+gbp_vim_workspace_addin_load (IdeWorkspaceAddin *addin,
+                              IdeWorkspace      *workspace)
+{
+  GbpVimWorkspaceAddin *self = (GbpVimWorkspaceAddin *)addin;
+  g_autofree char *keybindings = NULL;
+  PanelStatusbar *statusbar;
+  PangoAttrList *attrs;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  self->editor_settings = g_settings_new ("org.gnome.builder.editor");
+  keybindings = g_settings_get_string (self->editor_settings, "keybindings");
+  g_signal_connect_object (self->editor_settings,
+                           "changed::keybindings",
+                           G_CALLBACK (on_keybindings_changed_cb),
+                           self,
+                           G_CONNECT_SWAPPED);
+
+  self->active = g_strcmp0 (keybindings, "vim") == 0;
+
+  if (!self->active)
+    g_debug ("Vim plugin loaded but inactive as keybindings are currently \"%s\"",
+             keybindings);
+
+  attrs = pango_attr_list_new ();
+  pango_attr_list_insert (attrs, pango_attr_family_new ("Monospace"));
+
+  self->command_bar = g_object_new (GTK_TYPE_LABEL,
+                                    "attributes", attrs,
+                                    "hexpand", TRUE,
+                                    "selectable", TRUE,
+                                    "visible", FALSE,
+                                    "xalign", .0f,
+                                    NULL);
+  self->command = g_object_new (GTK_TYPE_LABEL,
+                                "attributes", attrs,
+                                "visible", FALSE,
+                                "xalign", 1.f,
+                                NULL);
+
+  statusbar = ide_workspace_get_statusbar (workspace);
+
+  panel_statusbar_add_prefix (statusbar, 10000, GTK_WIDGET (self->command_bar));
+  panel_statusbar_add_suffix (statusbar, 21000, GTK_WIDGET (self->command));
+
+  pango_attr_list_unref (attrs);
+
+  IDE_EXIT;
+}
+
+static void
+gbp_vim_workspace_addin_unload (IdeWorkspaceAddin *addin,
+                                IdeWorkspace      *workspace)
+{
+  GbpVimWorkspaceAddin *self = (GbpVimWorkspaceAddin *)addin;
+  PanelStatusbar *statusbar;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_WORKSPACE_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  statusbar = ide_workspace_get_statusbar (workspace);
+
+  panel_statusbar_remove (statusbar, GTK_WIDGET (self->command_bar));
+  panel_statusbar_remove (statusbar, GTK_WIDGET (self->command));
+
+  self->command_bar = NULL;
+  self->command = NULL;
+  self->active = FALSE;
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_ACTIVE]);
+
+  g_clear_object (&self->editor_settings);
+
+  IDE_EXIT;
+}
+
+static void
+workspace_addin_iface_init (IdeWorkspaceAddinInterface *iface)
+{
+  iface->load = gbp_vim_workspace_addin_load;
+  iface->unload = gbp_vim_workspace_addin_unload;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpVimWorkspaceAddin, gbp_vim_workspace_addin, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKSPACE_ADDIN, workspace_addin_iface_init))
+
+static void
+gbp_vim_workspace_addin_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  GbpVimWorkspaceAddin *self = GBP_VIM_WORKSPACE_ADDIN (object);
+
+  switch (prop_id)
+    {
+    case PROP_ACTIVE:
+      g_value_set_boolean (value, self->active);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_vim_workspace_addin_class_init (GbpVimWorkspaceAddinClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->get_property = gbp_vim_workspace_addin_get_property;
+
+  /**
+   * GbpVimWorkspaceAddin:active:
+   *
+   * The "active" property is bound by other vim plugin hooks so that they
+   * may all enable/disable together based on the current keybindings setting
+   * in org.gnome.builder.editor.
+   */
+  properties [PROP_ACTIVE] =
+    g_param_spec_boolean ("active",
+                          "Active",
+                          "If vim is the active keybindings for the application",
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_vim_workspace_addin_init (GbpVimWorkspaceAddin *self)
+{
+}
+
+void
+gbp_vim_workspace_addin_set_command (GbpVimWorkspaceAddin *self,
+                                     const char           *command)
+{
+  g_return_if_fail (GBP_IS_VIM_WORKSPACE_ADDIN (self));
+
+  if (self->command != NULL)
+    {
+      gtk_label_set_label (self->command, command);
+      gtk_widget_set_visible (GTK_WIDGET (self->command), !ide_str_empty0 (command));
+    }
+}
+
+void
+gbp_vim_workspace_addin_set_command_bar (GbpVimWorkspaceAddin *self,
+                                         const char           *command_bar)
+{
+  g_return_if_fail (GBP_IS_VIM_WORKSPACE_ADDIN (self));
+
+  if (self->command_bar != NULL)
+    {
+      gtk_label_set_label (self->command_bar, command_bar);
+      gtk_widget_set_visible (GTK_WIDGET (self->command_bar), !ide_str_empty0 (command_bar));
+    }
+}
+
+gboolean
+gbp_vim_workspace_addin_get_active (GbpVimWorkspaceAddin *self)
+{
+  g_return_val_if_fail (GBP_IS_VIM_WORKSPACE_ADDIN (self), FALSE);
+
+  return self->active;
+}
diff --git a/src/plugins/vim/gbp-vim-command.h b/src/plugins/vim/gbp-vim-workspace-addin.h
similarity index 51%
rename from src/plugins/vim/gbp-vim-command.h
rename to src/plugins/vim/gbp-vim-workspace-addin.h
index 79e5994d1..c03ecf0ed 100644
--- a/src/plugins/vim/gbp-vim-command.h
+++ b/src/plugins/vim/gbp-vim-workspace-addin.h
@@ -1,6 +1,6 @@
-/* gbp-vim-command.h
+/* gbp-vim-workspace-addin.h
  *
- * Copyright 2018-2019 Christian Hergert <chergert redhat com>
+ * Copyright 2022 Christian Hergert <chergert redhat 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
@@ -20,17 +20,18 @@
 
 #pragma once
 
-#include <libide-core.h>
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
-#define GBP_TYPE_VIM_COMMAND (gbp_vim_command_get_type())
+#define GBP_TYPE_VIM_WORKSPACE_ADDIN (gbp_vim_workspace_addin_get_type())
 
-G_DECLARE_FINAL_TYPE (GbpVimCommand, gbp_vim_command, GBP, VIM_COMMAND, IdeObject)
+G_DECLARE_FINAL_TYPE (GbpVimWorkspaceAddin, gbp_vim_workspace_addin, GBP, VIM_WORKSPACE_ADDIN, GObject)
 
-GbpVimCommand *gbp_vim_command_new (GtkWidget   *active_widget,
-                                    const gchar *typed_text,
-                                    const gchar *command,
-                                    const gchar *description);
+gboolean gbp_vim_workspace_addin_get_active      (GbpVimWorkspaceAddin *self);
+void     gbp_vim_workspace_addin_set_command     (GbpVimWorkspaceAddin *self,
+                                                  const char           *command);
+void     gbp_vim_workspace_addin_set_command_bar (GbpVimWorkspaceAddin *self,
+                                                  const char           *command_bar);
 
 G_END_DECLS
diff --git a/src/plugins/vim/meson.build b/src/plugins/vim/meson.build
index fcdcde818..8fdfd0080 100644
--- a/src/plugins/vim/meson.build
+++ b/src/plugins/vim/meson.build
@@ -1,9 +1,8 @@
 plugins_sources += files([
   'vim-plugin.c',
-  'gb-vim.c',
-  'gbp-vim-command.c',
-  'gbp-vim-command-provider.c',
+  'gbp-vim-editor-page-addin.c',
   'gbp-vim-preferences-addin.c',
+  'gbp-vim-workspace-addin.c',
 ])
 
 plugin_vim_resources = gnome.compile_resources(
diff --git a/src/plugins/vim/vim-plugin.c b/src/plugins/vim/vim-plugin.c
index 5a4cca438..417836cd2 100644
--- a/src/plugins/vim/vim-plugin.c
+++ b/src/plugins/vim/vim-plugin.c
@@ -23,19 +23,25 @@
 #include "config.h"
 
 #include <libpeas/peas.h>
+
 #include <libide-core.h>
 #include <libide-gui.h>
+#include <libide-editor.h>
 
-#include "gbp-vim-command-provider.h"
+#include "gbp-vim-editor-page-addin.h"
 #include "gbp-vim-preferences-addin.h"
+#include "gbp-vim-workspace-addin.h"
 
 _IDE_EXTERN void
 _gbp_vim_register_types (PeasObjectModule *module)
 {
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_EDITOR_PAGE_ADDIN,
+                                              GBP_TYPE_VIM_EDITOR_PAGE_ADDIN);
   peas_object_module_register_extension_type (module,
                                               IDE_TYPE_PREFERENCES_ADDIN,
                                               GBP_TYPE_VIM_PREFERENCES_ADDIN);
   peas_object_module_register_extension_type (module,
-                                              IDE_TYPE_COMMAND_PROVIDER,
-                                              GBP_TYPE_VIM_COMMAND_PROVIDER);
+                                              IDE_TYPE_WORKSPACE_ADDIN,
+                                              GBP_TYPE_VIM_WORKSPACE_ADDIN);
 }
diff --git a/src/plugins/vim/vim.gresource.xml b/src/plugins/vim/vim.gresource.xml
index 6d08eaaf2..6db9e6389 100644
--- a/src/plugins/vim/vim.gresource.xml
+++ b/src/plugins/vim/vim.gresource.xml
@@ -2,6 +2,5 @@
 <gresources>
   <gresource prefix="/plugins/vim">
     <file>vim.plugin</file>
-    <file>keybindings/vim.css</file>
   </gresource>
 </gresources>
diff --git a/src/plugins/vim/vim.plugin b/src/plugins/vim/vim.plugin
index 5d9d0ef21..6decdc4ad 100644
--- a/src/plugins/vim/vim.plugin
+++ b/src/plugins/vim/vim.plugin
@@ -1,9 +1,11 @@
 [Plugin]
 Authors=Christian Hergert <christian hergert me>
 Builtin=true
-Copyright=Copyright © 2015-2018 Christian Hergert
+Copyright=Copyright © 2015-2022 Christian Hergert
 Description=Emulation of various VIM features
 Embedded=_gbp_vim_register_types
-Hidden=true
 Module=vim
 Name=VIM Emulation
+X-Category=keybindings
+X-Workspace-Kind=editor;primary;
+X-Preferences-Kind=application;


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