[gtksourceview] Add "smart-backspace" property



commit b04a9f5cf5a85f11b3a5ee98da180214ac58e55b
Author: Paolo Borelli <pborelli gnome org>
Date:   Sun Jun 21 12:25:25 2015 +0200

    Add "smart-backspace" property
    
    Ported from gnome-builder code. Gedit has a smart-spaces plugin
    with the same logic, so it makes sense to consolidate the
    implementation in gtksourceview itself

 docs/reference/gtksourceview-3.0-sections.txt |    2 +
 gtksourceview/gtksourceview.c                 |  197 ++++++++++++++++++++++++-
 gtksourceview/gtksourceview.h                 |    5 +
 tests/test-widget.c                           |    8 +
 tests/test-widget.ui                          |   15 ++-
 5 files changed, 224 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/gtksourceview-3.0-sections.txt b/docs/reference/gtksourceview-3.0-sections.txt
index 7e18b88..01a6a46 100644
--- a/docs/reference/gtksourceview-3.0-sections.txt
+++ b/docs/reference/gtksourceview-3.0-sections.txt
@@ -852,6 +852,8 @@ gtk_source_view_get_insert_spaces_instead_of_tabs
 gtk_source_view_indent_lines
 gtk_source_view_unindent_lines
 gtk_source_view_get_visual_column
+gtk_source_view_set_smart_backspace
+gtk_source_view_get_smart_backspace
 gtk_source_view_set_smart_home_end
 gtk_source_view_get_smart_home_end
 gtk_source_view_set_mark_attributes
diff --git a/gtksourceview/gtksourceview.c b/gtksourceview/gtksourceview.c
index 8f0e1ea..2c98f3e 100644
--- a/gtksourceview/gtksourceview.c
+++ b/gtksourceview/gtksourceview.c
@@ -157,7 +157,8 @@ enum
        PROP_HIGHLIGHT_CURRENT_LINE,
        PROP_INDENT_ON_TAB,
        PROP_DRAW_SPACES,
-       PROP_BACKGROUND_PATTERN
+       PROP_BACKGROUND_PATTERN,
+       PROP_SMART_BACKSPACE
 };
 
 struct _GtkSourceViewPrivate
@@ -202,6 +203,7 @@ struct _GtkSourceViewPrivate
        guint style_scheme_applied : 1;
        guint current_line_color_set : 1;
        guint background_pattern_color_set : 1;
+       guint smart_backspace : 1;
 };
 
 typedef struct _MarkCategory MarkCategory;
@@ -652,6 +654,14 @@ gtk_source_view_class_init (GtkSourceViewClass *klass)
                                                            GTK_SOURCE_BACKGROUND_PATTERN_TYPE_NONE,
                                                            G_PARAM_READWRITE));
 
+       g_object_class_install_property (object_class,
+                                        PROP_SMART_BACKSPACE,
+                                        g_param_spec_boolean ("smart-backspace",
+                                                              _("Smart Backspace"),
+                                                              _("Whether smart Backspace should be used."),
+                                                              FALSE,
+                                                              G_PARAM_READWRITE));
+
        signals[UNDO] =
                g_signal_new ("undo",
                              G_TYPE_FROM_CLASS (klass),
@@ -1114,6 +1124,10 @@ gtk_source_view_set_property (GObject      *object,
                        gtk_source_view_set_background_pattern (view, g_value_get_enum (value));
                        break;
 
+               case PROP_SMART_BACKSPACE:
+                       gtk_source_view_set_smart_backspace (view, g_value_get_boolean (value));
+                       break;
+
                default:
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                        break;
@@ -1137,6 +1151,7 @@ gtk_source_view_get_property (GObject    *object,
                case PROP_COMPLETION:
                        g_value_set_object (value, gtk_source_view_get_completion (view));
                        break;
+
                case PROP_SHOW_LINE_NUMBERS:
                        g_value_set_boolean (value, gtk_source_view_get_show_line_numbers (view));
                        break;
@@ -1189,6 +1204,10 @@ gtk_source_view_get_property (GObject    *object,
                        g_value_set_enum (value, gtk_source_view_get_background_pattern (view));
                        break;
 
+               case PROP_SMART_BACKSPACE:
+                       g_value_set_boolean (value, gtk_source_view_get_smart_backspace (view));
+                       break;
+
                default:
                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                        break;
@@ -4025,6 +4044,128 @@ gtk_source_view_move_lines (GtkSourceView *view,
 }
 
 static gboolean
+gtk_source_view_do_smart_backspace (GtkSourceView *view,
+                                    guint          modifiers)
+{
+       GtkTextBuffer *buffer;
+       GtkTextIter insert;
+       GtkTextIter end;
+       GtkTextIter tmp;
+       guint visual_column;
+       gint indent_width;
+       gint tab_width;
+
+       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+       if (gtk_text_buffer_get_selection_bounds (buffer, &insert, &end))
+       {
+               return FALSE;
+       }
+
+       if ((modifiers & GDK_CONTROL_MASK) != 0)
+       {
+               /*
+                * A <Control>BackSpace at the beginning of the line should only move us to the
+                * end of the previous line. Anything more than that is non-obvious because it requires
+                * looking in a position other than where the cursor is.
+                */
+               if ((gtk_text_iter_get_line_offset (&insert) == 0) && (gtk_text_iter_get_line (&insert) > 0))
+               {
+                       gtk_text_buffer_begin_user_action (buffer);
+                       gtk_text_iter_backward_char (&insert);
+                       gtk_text_buffer_delete (buffer, &insert, &end);
+                       gtk_text_buffer_end_user_action (buffer);
+
+                       return TRUE;
+               }
+       }
+
+       /* if the line isn't empty up to our cursor, ignore */
+       tmp = insert;
+       while (TRUE)
+       {
+               gunichar ch;
+
+               ch = gtk_text_iter_get_char (&tmp);
+
+               if ((ch != 0) && !g_unichar_isspace (ch))
+               {
+                       return FALSE;
+               }
+
+               if (gtk_text_iter_starts_line (&tmp))
+               {
+                       break;
+               }
+
+               gtk_text_iter_backward_char (&tmp);
+       }
+
+       /*
+        * If <Control>BackSpace was specified, delete up to the zero position.
+        */
+       if ((modifiers & GDK_CONTROL_MASK) != 0)
+       {
+               gtk_text_buffer_begin_user_action (buffer);
+               gtk_text_iter_set_line_offset (&insert, 0);
+               gtk_text_buffer_delete (buffer, &insert, &end);
+               gtk_text_buffer_end_user_action (buffer);
+
+               return TRUE;
+       }
+
+       visual_column = gtk_source_view_get_visual_column (view, &insert);
+       indent_width = gtk_source_view_get_indent_width (view);
+       tab_width = gtk_source_view_get_tab_width (view);
+       if (indent_width <= 0)
+       {
+               indent_width = tab_width;
+       }
+
+       if (visual_column < indent_width)
+       {
+               return FALSE;
+       }
+
+       if ((visual_column % indent_width) == 0)
+       {
+               gint target_column = visual_column - indent_width;
+               gunichar ch;
+
+               g_assert (target_column >= 0);
+
+               while (gtk_source_view_get_visual_column (view, &insert) > target_column)
+               {
+                       gtk_text_iter_backward_char (&insert);
+                       ch = gtk_text_iter_get_char (&insert);
+
+                       if (!g_unichar_isspace (ch))
+                       {
+                               return FALSE;
+                       }
+               }
+
+               ch = gtk_text_iter_get_char (&insert);
+               if (!g_unichar_isspace (ch))
+               {
+                       return FALSE;
+               }
+
+               gtk_text_buffer_begin_user_action (buffer);
+               gtk_text_buffer_delete (buffer, &insert, &end);
+               while (gtk_source_view_get_visual_column (view, &insert) < target_column)
+               {
+                       gtk_text_buffer_insert (buffer, &insert, " ", 1);
+               }
+               gtk_text_buffer_end_user_action (buffer);
+
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+static gboolean
 gtk_source_view_key_press_event (GtkWidget   *widget,
                                 GdkEventKey *event)
 {
@@ -4131,7 +4272,15 @@ gtk_source_view_key_press_event (GtkWidget   *widget,
                }
 
                insert_tab_or_spaces (view, &s, &e);
-               return TRUE;
+               return TRUE;
+       }
+
+       if ((key == GDK_KEY_BackSpace) && view->priv->smart_backspace)
+       {
+               if (gtk_source_view_do_smart_backspace (view, (event->state & modifiers)))
+               {
+                       return TRUE;
+               }
        }
 
        return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->key_press_event (widget, event);
@@ -4464,6 +4613,50 @@ gtk_source_view_set_right_margin_position (GtkSourceView *view,
 }
 
 /**
+ * gtk_source_view_set_smart_backspace:
+ * @view: a #GtkSourceView.
+ * @smart_backspace: whether to enable smart Backspace handling.
+ *
+ * When set to %TRUE, pressing the Backspace key will try to delete spaces
+ * up to the previous tab stop.
+ *
+ * Since: 3.18
+ */
+void
+gtk_source_view_set_smart_backspace (GtkSourceView *view,
+                                     gboolean       smart_backspace)
+{
+       g_return_if_fail (GTK_SOURCE_IS_VIEW (view));
+
+       smart_backspace = smart_backspace != FALSE;
+
+       if (smart_backspace != view->priv->smart_backspace)
+       {
+               view->priv->smart_backspace = smart_backspace;
+               g_object_notify (G_OBJECT (view), "smart-backspace");
+       }
+}
+
+/**
+ * gtk_source_view_get_smart_backspace:
+ * @view: a #GtkSourceView.
+ *
+ * Returns %TRUE if pressing the Backspace key will try to delete spaces
+ * up to the previous tab stop.
+ *
+ * Returns: %TRUE if smart Backspace handling is enabled.
+ *
+ * Since: 3.18
+ */
+gboolean
+gtk_source_view_get_smart_backspace (GtkSourceView *view)
+{
+       g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), FALSE);
+
+       return view->priv->smart_backspace;
+}
+
+/**
  * gtk_source_view_set_smart_home_end:
  * @view: a #GtkSourceView.
  * @smart_home_end: the desired behavior among #GtkSourceSmartHomeEndType.
diff --git a/gtksourceview/gtksourceview.h b/gtksourceview/gtksourceview.h
index c89358e..2e2e9db 100644
--- a/gtksourceview/gtksourceview.h
+++ b/gtksourceview/gtksourceview.h
@@ -208,6 +208,11 @@ GtkSourceMarkAttributes *
                                                          const gchar             *category,
                                                          gint                    *priority);
 
+void            gtk_source_view_set_smart_backspace    (GtkSourceView   *view,
+                                                        gboolean        smart_backspace);
+
+gboolean        gtk_source_view_get_smart_backspace    (GtkSourceView   *view);
+
 void            gtk_source_view_set_smart_home_end     (GtkSourceView             *view,
                                                         GtkSourceSmartHomeEndType  smart_home_end);
 
diff --git a/tests/test-widget.c b/tests/test-widget.c
index e5a6f66..d399046 100644
--- a/tests/test-widget.c
+++ b/tests/test-widget.c
@@ -54,6 +54,7 @@ struct _TestWidgetPrivate
        GtkSourceFile *file;
        GtkSourceMap *map;
        GtkCheckButton *show_map_checkbutton;
+       GtkCheckButton *smart_backspace_checkbutton;
        GtkCheckButton *indent_width_checkbutton;
        GtkSpinButton *indent_width_spinbutton;
        GtkLabel *cursor_position_info;
@@ -984,6 +985,7 @@ test_widget_class_init (TestWidgetClass *klass)
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, view);
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, map);
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, show_map_checkbutton);
+       gtk_widget_class_bind_template_child_private (widget_class, TestWidget, smart_backspace_checkbutton);
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, indent_width_checkbutton);
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, indent_width_spinbutton);
        gtk_widget_class_bind_template_child_private (widget_class, TestWidget, cursor_position_info);
@@ -1047,6 +1049,12 @@ test_widget_init (TestWidget *self)
                                "visible",
                                G_BINDING_SYNC_CREATE);
 
+       g_object_bind_property (self->priv->smart_backspace_checkbutton,
+                               "active",
+                               self->priv->view,
+                               "smart-backspace",
+                               G_BINDING_SYNC_CREATE);
+
        g_signal_connect (self->priv->background_pattern,
                          "changed",
                          G_CALLBACK (on_background_pattern_changed),
diff --git a/tests/test-widget.ui b/tests/test-widget.ui
index 82c4f40..1e26770 100644
--- a/tests/test-widget.ui
+++ b/tests/test-widget.ui
@@ -227,6 +227,19 @@
               </packing>
             </child>
             <child>
+              <object class="GtkCheckButton" id="smart_backspace_checkbutton">
+                <property name="label">Smart Backspace</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">10</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkGrid" id="grid8">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
@@ -263,7 +276,7 @@
               </object>
               <packing>
                 <property name="left_attach">0</property>
-                <property name="top_attach">10</property>
+                <property name="top_attach">11</property>
               </packing>
             </child>
             <child>


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