[gnome-builder/change-monitor] editor: add utility to track changes in the source buffer.
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/change-monitor] editor: add utility to track changes in the source buffer.
- Date: Sat, 13 Sep 2014 00:21:17 +0000 (UTC)
commit b377be37261ea15cae15b6f3ed3a0a43ffee9994
Author: Christian Hergert <christian hergert me>
Date: Fri Sep 12 17:21:06 2014 -0700
editor: add utility to track changes in the source buffer.
This only handles insertions at the moment. Followup commits will add
support for handling deletions from the buffer.
src/editor/gb-editor-commands.c | 7 +
src/editor/gb-editor-tab-private.h | 7 +
src/editor/gb-editor-tab.c | 18 +
src/editor/gb-source-change-gutter-renderer.c | 189 ++++++++++++
src/editor/gb-source-change-gutter-renderer.h | 55 ++++
src/editor/gb-source-change-monitor.c | 410 +++++++++++++++++++++++++
src/editor/gb-source-change-monitor.h | 66 ++++
src/gnome-builder.mk | 4 +
src/resources/ui/gb-editor-tab.ui | 3 +
9 files changed, 759 insertions(+), 0 deletions(-)
---
diff --git a/src/editor/gb-editor-commands.c b/src/editor/gb-editor-commands.c
index 27fa6a1..f3e477c 100644
--- a/src/editor/gb-editor-commands.c
+++ b/src/editor/gb-editor-commands.c
@@ -366,6 +366,9 @@ on_load_cb (GtkSourceFileLoader *loader,
g_clear_error (&error);
}
+ gb_source_change_monitor_reset (tab->priv->change_monitor);
+ gtk_source_gutter_renderer_set_visible (tab->priv->change_renderer, TRUE);
+
g_object_unref (tab);
}
@@ -386,6 +389,8 @@ gb_editor_tab_open_file (GbEditorTab *tab,
loader = gtk_source_file_loader_new (GTK_SOURCE_BUFFER (priv->document),
priv->file);
+ gtk_source_gutter_renderer_set_visible (priv->change_renderer, FALSE);
+
gtk_source_file_loader_load_async (loader,
G_PRIORITY_DEFAULT,
NULL, /* TODO: Cancellable */
@@ -633,6 +638,8 @@ gb_editor_commands_new_tab (GbEditorWorkspace *workspace,
"position", &page,
NULL);
gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), page);
+
+ gtk_widget_grab_focus (GTK_WIDGET (tab));
}
static gboolean
diff --git a/src/editor/gb-editor-tab-private.h b/src/editor/gb-editor-tab-private.h
index 318dc83..c1c26b3 100644
--- a/src/editor/gb-editor-tab-private.h
+++ b/src/editor/gb-editor-tab-private.h
@@ -28,6 +28,7 @@
#include "gb-editor-settings.h"
#include "gb-markdown-preview.h"
#include "gb-notebook.h"
+#include "gb-source-change-monitor.h"
#include "gb-source-search-highlighter.h"
#include "gb-source-snippet-completion-provider.h"
#include "gb-source-view.h"
@@ -56,6 +57,12 @@ struct _GbEditorTabPrivate
GtkSourceSearchContext *search_context;
/*
+ * Change (add, change, etc) tracking of the editor.
+ */
+ GbSourceChangeMonitor *change_monitor;
+ GtkSourceGutterRenderer *change_renderer;
+
+ /*
* Tab related settings.
*/
GbEditorSettings *settings;
diff --git a/src/editor/gb-editor-tab.c b/src/editor/gb-editor-tab.c
index a9ec8b8..6a82a12 100644
--- a/src/editor/gb-editor-tab.c
+++ b/src/editor/gb-editor-tab.c
@@ -24,6 +24,7 @@
#include "gb-editor-tab-private.h"
#include "gb-log.h"
#include "gb-rgba.h"
+#include "gb-source-change-gutter-renderer.h"
#include "gb-source-snippet.h"
#include "gb-source-snippets-manager.h"
#include "gb-source-snippets.h"
@@ -1027,6 +1028,21 @@ gb_editor_tab_constructed (GObject *object)
"language", G_BINDING_SYNC_CREATE,
transform_file_to_language, NULL, tab, NULL);
+ {
+ GtkSourceGutter *gutter;
+
+ gutter = gtk_source_view_get_gutter (GTK_SOURCE_VIEW (priv->source_view),
+ GTK_TEXT_WINDOW_LEFT);
+ priv->change_renderer =
+ g_object_new (GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER,
+ "change-monitor", priv->change_monitor,
+ "size", 3,
+ "visible", TRUE,
+ "xpad", 3,
+ NULL);
+ gtk_source_gutter_insert (gutter, priv->change_renderer, 0);
+ }
+
gb_editor_tab_cursor_moved (tab, priv->document);
EXIT;
@@ -1208,6 +1224,7 @@ gb_editor_tab_class_init (GbEditorTabClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, floating_bar);
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, document);
+ gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, change_monitor);
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, file);
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, go_down_button);
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, go_up_button);
@@ -1224,6 +1241,7 @@ gb_editor_tab_class_init (GbEditorTabClass *klass)
gtk_widget_class_bind_template_child_private (widget_class, GbEditorTab, source_view);
g_type_ensure (GB_TYPE_EDITOR_DOCUMENT);
+ g_type_ensure (GB_TYPE_SOURCE_CHANGE_MONITOR);
g_type_ensure (GB_TYPE_SOURCE_VIEW);
g_type_ensure (GB_TYPE_SOURCE_SNIPPET_COMPLETION_PROVIDER);
g_type_ensure (GB_TYPE_SOURCE_SEARCH_HIGHLIGHTER);
diff --git a/src/editor/gb-source-change-gutter-renderer.c b/src/editor/gb-source-change-gutter-renderer.c
new file mode 100644
index 0000000..0c8597d
--- /dev/null
+++ b/src/editor/gb-source-change-gutter-renderer.c
@@ -0,0 +1,189 @@
+/* gb-source-change-gutter-renderer.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * 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/>.
+ */
+
+#include <glib/gi18n.h>
+
+#include "gb-source-change-gutter-renderer.h"
+#include "gb-source-change-monitor.h"
+
+struct _GbSourceChangeGutterRendererPrivate
+{
+ GbSourceChangeMonitor *change_monitor;
+};
+
+enum
+{
+ PROP_0,
+ PROP_CHANGE_MONITOR,
+ LAST_PROP
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbSourceChangeGutterRenderer,
+ gb_source_change_gutter_renderer,
+ GTK_SOURCE_TYPE_GUTTER_RENDERER)
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+GbSourceChangeMonitor *
+gb_source_change_gutter_renderer_get_change_monitor (GbSourceChangeGutterRenderer *renderer)
+{
+ g_return_val_if_fail (GB_IS_SOURCE_CHANGE_GUTTER_RENDERER (renderer), NULL);
+
+ return renderer->priv->change_monitor;
+}
+
+static void
+gb_source_change_gutter_renderer_set_change_monitor (GbSourceChangeGutterRenderer *renderer,
+ GbSourceChangeMonitor *monitor)
+{
+ GbSourceChangeGutterRendererPrivate *priv;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_GUTTER_RENDERER (renderer));
+ g_return_if_fail (!monitor || GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ priv = renderer->priv;
+
+ if (priv->change_monitor)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (monitor),
+ (gpointer *)&priv->change_monitor);
+ priv->change_monitor = NULL;
+ }
+
+ if (monitor)
+ {
+ priv->change_monitor = monitor;
+ g_object_add_weak_pointer (G_OBJECT (monitor),
+ (gpointer *)&priv->change_monitor);
+ }
+}
+
+static void
+gb_source_change_gutter_renderer_draw (GtkSourceGutterRenderer *renderer,
+ cairo_t *cr,
+ GdkRectangle *bg_area,
+ GdkRectangle *cell_area,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkSourceGutterRendererState state)
+{
+ GbSourceChangeGutterRendererPrivate *priv;
+ GbSourceChangeFlags flags;
+ GdkRGBA rgba;
+ guint lineno;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_GUTTER_RENDERER (renderer));
+ g_return_if_fail (cr);
+ g_return_if_fail (bg_area);
+ g_return_if_fail (cell_area);
+ g_return_if_fail (begin);
+ g_return_if_fail (end);
+
+ priv = GB_SOURCE_CHANGE_GUTTER_RENDERER (renderer)->priv;
+
+ lineno = gtk_text_iter_get_line (begin);
+ flags = gb_source_change_monitor_get_line (priv->change_monitor, lineno);
+
+ if (!flags)
+ return;
+
+ if ((flags & GB_SOURCE_CHANGE_ADDED) != 0)
+ gdk_rgba_parse (&rgba, "#8ae234");
+
+ if ((flags & GB_SOURCE_CHANGE_CHANGED) != 0)
+ gdk_rgba_parse (&rgba, "#fcaf3e");
+
+ gdk_cairo_rectangle (cr, cell_area);
+ gdk_cairo_set_source_rgba (cr, &rgba);
+ cairo_fill (cr);
+}
+
+static void
+gb_source_change_gutter_renderer_dispose (GObject *object)
+{
+ GbSourceChangeGutterRenderer *renderer = (GbSourceChangeGutterRenderer *)object;
+
+ gb_source_change_gutter_renderer_set_change_monitor (renderer, NULL);
+
+ G_OBJECT_CLASS (gb_source_change_gutter_renderer_parent_class)->dispose (object);
+}
+
+static void
+gb_source_change_gutter_renderer_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbSourceChangeGutterRenderer *renderer = GB_SOURCE_CHANGE_GUTTER_RENDERER (object);
+
+ switch (prop_id) {
+ case PROP_CHANGE_MONITOR:
+ g_value_set_object (value, renderer->priv->change_monitor);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_source_change_gutter_renderer_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbSourceChangeGutterRenderer *renderer = GB_SOURCE_CHANGE_GUTTER_RENDERER (object);
+
+ switch (prop_id) {
+ case PROP_CHANGE_MONITOR:
+ gb_source_change_gutter_renderer_set_change_monitor (
+ renderer, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_source_change_gutter_renderer_class_init (GbSourceChangeGutterRendererClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkSourceGutterRendererClass *renderer_class = GTK_SOURCE_GUTTER_RENDERER_CLASS (klass);
+
+ object_class->dispose = gb_source_change_gutter_renderer_dispose;
+ object_class->get_property = gb_source_change_gutter_renderer_get_property;
+ object_class->set_property = gb_source_change_gutter_renderer_set_property;
+
+ renderer_class->draw = gb_source_change_gutter_renderer_draw;
+
+ gParamSpecs [PROP_CHANGE_MONITOR] =
+ g_param_spec_object ("change-monitor",
+ _("Change Monitor"),
+ _("The change monitor for the gutter renderer."),
+ GB_TYPE_SOURCE_CHANGE_MONITOR,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_CHANGE_MONITOR,
+ gParamSpecs [PROP_CHANGE_MONITOR]);
+}
+
+static void
+gb_source_change_gutter_renderer_init (GbSourceChangeGutterRenderer *renderer)
+{
+ renderer->priv = gb_source_change_gutter_renderer_get_instance_private (renderer);
+}
diff --git a/src/editor/gb-source-change-gutter-renderer.h b/src/editor/gb-source-change-gutter-renderer.h
new file mode 100644
index 0000000..0aaa077
--- /dev/null
+++ b/src/editor/gb-source-change-gutter-renderer.h
@@ -0,0 +1,55 @@
+/* gb-source-change-gutter-renderer.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This file 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.
+ *
+ * This file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SOURCE_CHANGE_GUTTER_RENDERER_H
+#define GB_SOURCE_CHANGE_GUTTER_RENDERER_H
+
+#include <gtksourceview/gtksource.h>
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER (gb_source_change_gutter_renderer_get_type())
+#define GB_SOURCE_CHANGE_GUTTER_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER, GbSourceChangeGutterRenderer))
+#define GB_SOURCE_CHANGE_GUTTER_RENDERER_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER, GbSourceChangeGutterRenderer const))
+#define GB_SOURCE_CHANGE_GUTTER_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER, GbSourceChangeGutterRendererClass))
+#define GB_IS_SOURCE_CHANGE_GUTTER_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER))
+#define GB_IS_SOURCE_CHANGE_GUTTER_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER))
+#define GB_SOURCE_CHANGE_GUTTER_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GB_TYPE_SOURCE_CHANGE_GUTTER_RENDERER, GbSourceChangeGutterRendererClass))
+
+typedef struct _GbSourceChangeGutterRenderer GbSourceChangeGutterRenderer;
+typedef struct _GbSourceChangeGutterRendererClass GbSourceChangeGutterRendererClass;
+typedef struct _GbSourceChangeGutterRendererPrivate GbSourceChangeGutterRendererPrivate;
+
+struct _GbSourceChangeGutterRenderer
+{
+ GtkSourceGutterRenderer parent;
+
+ /*< private >*/
+ GbSourceChangeGutterRendererPrivate *priv;
+};
+
+struct _GbSourceChangeGutterRendererClass
+{
+ GtkSourceGutterRendererClass parent_class;
+};
+
+GType gb_source_change_gutter_renderer_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* GB_SOURCE_CHANGE_GUTTER_RENDERER_H */
diff --git a/src/editor/gb-source-change-monitor.c b/src/editor/gb-source-change-monitor.c
new file mode 100644
index 0000000..d5089ee
--- /dev/null
+++ b/src/editor/gb-source-change-monitor.c
@@ -0,0 +1,410 @@
+/* gb-source-change-monitor.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This file 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.
+ *
+ * This file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "change-monitor"
+
+#include <glib/gi18n.h>
+
+#include "gb-log.h"
+#include "gb-source-change-monitor.h"
+
+struct _GbSourceChangeMonitorPrivate
+{
+ GtkTextBuffer *buffer;
+ GArray *state;
+
+ guint delete_range_before_handler;
+ guint delete_range_after_handler;
+ guint insert_text_before_handler;
+ guint insert_text_after_handler;
+};
+
+enum
+{
+ PROP_0,
+ PROP_BUFFER,
+ LAST_PROP
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (GbSourceChangeMonitor,
+ gb_source_change_monitor,
+ G_TYPE_OBJECT)
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+
+static void
+gb_source_change_monitor_insert (GbSourceChangeMonitor *monitor,
+ guint line,
+ GbSourceChangeFlags flags)
+{
+ guint8 byte = (guint8)flags;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ if (line == monitor->priv->state->len)
+ g_array_append_val (monitor->priv->state, byte);
+ else
+ g_array_insert_val (monitor->priv->state, line, byte);
+}
+
+static void
+gb_source_change_monitor_set_line (GbSourceChangeMonitor *monitor,
+ guint line,
+ GbSourceChangeFlags flags)
+{
+ GbSourceChangeFlags old_flags;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (line < monitor->priv->state->len);
+
+ old_flags = g_array_index (monitor->priv->state, guint8, line);
+
+ /*
+ * Don't allow ourselves to go from "added" to "changed" unless we
+ * previously did not have a dirty bit.
+ */
+ if (((old_flags & GB_SOURCE_CHANGE_ADDED) != 0) &&
+ ((old_flags & GB_SOURCE_CHANGE_DIRTY) != 0))
+ {
+ flags &= ~GB_SOURCE_CHANGE_CHANGED;
+ flags |= GB_SOURCE_CHANGE_ADDED;
+ }
+
+ g_array_index (monitor->priv->state, guint8, line) = (guint8)flags;
+}
+
+static void
+gb_source_change_monitor_ensure_bounds (GbSourceChangeMonitor *monitor)
+{
+ GbSourceChangeMonitorPrivate *priv;
+ GtkTextIter begin;
+ GtkTextIter end;
+ guint line;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ priv = monitor->priv;
+
+ gtk_text_buffer_get_bounds (priv->buffer, &begin, &end);
+
+ line = gtk_text_iter_get_line (&end);
+
+ if (line + 1 > priv->state->len)
+ g_array_set_size (priv->state, line + 1);
+}
+
+static void
+on_delete_range_before_cb (GbSourceChangeMonitor *monitor,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+}
+
+static void
+on_delete_range_after_cb (GbSourceChangeMonitor *monitor,
+ GtkTextIter *begin,
+ GtkTextIter *end,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+}
+
+static void
+on_insert_text_before_cb (GbSourceChangeMonitor *monitor,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+}
+
+static void
+on_insert_text_after_cb (GbSourceChangeMonitor *monitor,
+ GtkTextIter *location,
+ gchar *text,
+ gint len,
+ GtkTextBuffer *buffer)
+{
+ GbSourceChangeFlags flags = 0;
+ guint line;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
+
+ line = gtk_text_iter_get_line (location);
+
+ if (g_strcmp0 (text, "\n") == 0)
+ {
+ flags = (GB_SOURCE_CHANGE_ADDED | GB_SOURCE_CHANGE_DIRTY);
+ gb_source_change_monitor_insert (monitor, line, flags);
+ }
+ else if (strchr (text, '\n') == NULL)
+ {
+ flags = (GB_SOURCE_CHANGE_CHANGED | GB_SOURCE_CHANGE_DIRTY);
+ gb_source_change_monitor_set_line (monitor, line, flags);
+ }
+ else
+ {
+ GtkTextIter end;
+ GtkTextIter iter;
+ guint last_line = line;
+
+ len = g_utf8_strlen (text, len);
+
+ gtk_text_iter_assign (&iter, location);
+ gtk_text_iter_assign (&end, location);
+ gtk_text_iter_backward_chars (&iter, len);
+
+ while (gtk_text_iter_compare (&iter, &end) <= 0)
+ {
+ line = gtk_text_iter_get_line (&iter);
+
+ if (line != last_line)
+ {
+ flags = (GB_SOURCE_CHANGE_ADDED | GB_SOURCE_CHANGE_DIRTY);
+ gb_source_change_monitor_insert (monitor, line, flags);
+ last_line = line;
+ }
+
+ if (!gtk_text_iter_forward_char (&iter))
+ break;
+ }
+ }
+
+ gb_source_change_monitor_ensure_bounds (monitor);
+}
+
+GbSourceChangeFlags
+gb_source_change_monitor_get_line (GbSourceChangeMonitor *monitor,
+ guint lineno)
+{
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ if (lineno < monitor->priv->state->len)
+ return monitor->priv->state->data [lineno];
+
+ g_warning ("No such line: %u", lineno);
+
+ return 0;
+}
+
+void
+gb_source_change_monitor_reset (GbSourceChangeMonitor *monitor)
+{
+ GbSourceChangeMonitorPrivate *priv;
+ GtkTextIter begin;
+ GtkTextIter end;
+ guint line;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ priv = monitor->priv;
+
+ if (priv->buffer)
+ {
+ gtk_text_buffer_get_bounds (priv->buffer, &begin, &end);
+ line = gtk_text_iter_get_line (&end);
+ g_array_set_size (priv->state, line + 1);
+ memset (priv->state->data, 0, priv->state->len);
+ }
+}
+
+GtkTextBuffer *
+gb_source_change_monitor_get_buffer (GbSourceChangeMonitor *monitor)
+{
+ g_return_val_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor), NULL);
+
+ return monitor->priv->buffer;
+}
+
+static void
+gb_source_change_monitor_set_buffer (GbSourceChangeMonitor *monitor,
+ GtkTextBuffer *buffer,
+ gboolean notify)
+{
+ GbSourceChangeMonitorPrivate *priv;
+
+ ENTRY;
+
+ g_return_if_fail (GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+ g_return_if_fail (!buffer || GB_IS_SOURCE_CHANGE_MONITOR (monitor));
+
+ priv = monitor->priv;
+
+ if (priv->buffer)
+ {
+ g_signal_handler_disconnect (priv->buffer,
+ priv->delete_range_before_handler);
+ g_signal_handler_disconnect (priv->buffer,
+ priv->delete_range_after_handler);
+ g_signal_handler_disconnect (priv->buffer,
+ priv->insert_text_before_handler);
+ g_signal_handler_disconnect (priv->buffer,
+ priv->insert_text_after_handler);
+ priv->delete_range_before_handler = 0;
+ priv->delete_range_after_handler = 0;
+ priv->insert_text_before_handler = 0;
+ priv->insert_text_after_handler = 0;
+ g_clear_object (&priv->buffer);
+ }
+
+ if (buffer)
+ {
+ priv->buffer = g_object_ref (buffer);
+ priv->delete_range_before_handler =
+ g_signal_connect_object (priv->buffer,
+ "delete-range",
+ G_CALLBACK (on_delete_range_before_cb),
+ monitor,
+ G_CONNECT_SWAPPED);
+ priv->delete_range_after_handler =
+ g_signal_connect_object (priv->buffer,
+ "delete-range",
+ G_CALLBACK (on_delete_range_after_cb),
+ monitor,
+ (G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+ priv->insert_text_before_handler =
+ g_signal_connect_object (priv->buffer,
+ "insert-text",
+ G_CALLBACK (on_insert_text_before_cb),
+ monitor,
+ G_CONNECT_SWAPPED);
+ priv->insert_text_after_handler =
+ g_signal_connect_object (priv->buffer,
+ "insert-text",
+ G_CALLBACK (on_insert_text_after_cb),
+ monitor,
+ (G_CONNECT_SWAPPED | G_CONNECT_AFTER));
+
+ gb_source_change_monitor_ensure_bounds (monitor);
+ }
+
+ if (notify)
+ g_object_notify_by_pspec (G_OBJECT (monitor), gParamSpecs [PROP_BUFFER]);
+
+ EXIT;
+}
+
+static void
+gb_source_change_monitor_dispose (GObject *object)
+{
+ ENTRY;
+
+ gb_source_change_monitor_set_buffer (GB_SOURCE_CHANGE_MONITOR (object),
+ NULL, FALSE);
+
+ G_OBJECT_CLASS (gb_source_change_monitor_parent_class)->dispose (object);
+
+ EXIT;
+}
+
+static void
+gb_source_change_monitor_finalize (GObject *object)
+{
+ GbSourceChangeMonitorPrivate *priv;
+
+ ENTRY;
+
+ priv = GB_SOURCE_CHANGE_MONITOR (object)->priv;
+
+ g_clear_pointer (&priv->state, g_array_unref);
+
+ G_OBJECT_CLASS (gb_source_change_monitor_parent_class)->finalize (object);
+
+ EXIT;
+}
+
+static void
+gb_source_change_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbSourceChangeMonitor *monitor = GB_SOURCE_CHANGE_MONITOR (object);
+
+ switch (prop_id) {
+ case PROP_BUFFER:
+ g_value_set_object (value,
+ gb_source_change_monitor_get_buffer (monitor));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_source_change_monitor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbSourceChangeMonitor *monitor = GB_SOURCE_CHANGE_MONITOR (object);
+
+ switch (prop_id) {
+ case PROP_BUFFER:
+ gb_source_change_monitor_set_buffer (monitor,
+ g_value_get_object (value),
+ TRUE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_source_change_monitor_class_init (GbSourceChangeMonitorClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = gb_source_change_monitor_dispose;
+ object_class->finalize = gb_source_change_monitor_finalize;
+ object_class->get_property = gb_source_change_monitor_get_property;
+ object_class->set_property = gb_source_change_monitor_set_property;
+
+ gParamSpecs [PROP_BUFFER] =
+ g_param_spec_object ("buffer",
+ _("Buffer"),
+ _("The text buffer to monitor."),
+ GTK_TYPE_TEXT_BUFFER,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_BUFFER,
+ gParamSpecs [PROP_BUFFER]);
+}
+
+static void
+gb_source_change_monitor_init (GbSourceChangeMonitor *monitor)
+{
+ ENTRY;
+
+ monitor->priv = gb_source_change_monitor_get_instance_private (monitor);
+
+ monitor->priv->state = g_array_new (FALSE, TRUE, sizeof (guint8));
+ g_array_set_size (monitor->priv->state, 1);
+
+ EXIT;
+}
diff --git a/src/editor/gb-source-change-monitor.h b/src/editor/gb-source-change-monitor.h
new file mode 100644
index 0000000..91d310a
--- /dev/null
+++ b/src/editor/gb-source-change-monitor.h
@@ -0,0 +1,66 @@
+/* gb-source-change-monitor.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This file 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.
+ *
+ * This file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GB_SOURCE_CHANGE_MONITOR_H
+#define GB_SOURCE_CHANGE_MONITOR_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GB_TYPE_SOURCE_CHANGE_MONITOR (gb_source_change_monitor_get_type())
+#define GB_SOURCE_CHANGE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_CHANGE_MONITOR, GbSourceChangeMonitor))
+#define GB_SOURCE_CHANGE_MONITOR_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
GB_TYPE_SOURCE_CHANGE_MONITOR, GbSourceChangeMonitor const))
+#define GB_SOURCE_CHANGE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
GB_TYPE_SOURCE_CHANGE_MONITOR, GbSourceChangeMonitorClass))
+#define GB_IS_SOURCE_CHANGE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
GB_TYPE_SOURCE_CHANGE_MONITOR))
+#define GB_IS_SOURCE_CHANGE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),
GB_TYPE_SOURCE_CHANGE_MONITOR))
+#define GB_SOURCE_CHANGE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
GB_TYPE_SOURCE_CHANGE_MONITOR, GbSourceChangeMonitorClass))
+
+typedef struct _GbSourceChangeMonitor GbSourceChangeMonitor;
+typedef struct _GbSourceChangeMonitorClass GbSourceChangeMonitorClass;
+typedef struct _GbSourceChangeMonitorPrivate GbSourceChangeMonitorPrivate;
+
+typedef enum
+{
+ GB_SOURCE_CHANGE_DIRTY = 1 << 0,
+ GB_SOURCE_CHANGE_ADDED = 1 << 1,
+ GB_SOURCE_CHANGE_CHANGED = 1 << 2,
+} GbSourceChangeFlags;
+
+struct _GbSourceChangeMonitor
+{
+ GObject parent;
+
+ /*< private >*/
+ GbSourceChangeMonitorPrivate *priv;
+};
+
+struct _GbSourceChangeMonitorClass
+{
+ GObjectClass parent_class;
+};
+
+GType gb_source_change_monitor_get_type (void) G_GNUC_CONST;
+GbSourceChangeMonitor *gb_source_change_monitor_new (GtkTextBuffer *buffer);
+GbSourceChangeFlags gb_source_change_monitor_get_line (GbSourceChangeMonitor *monitor,
+ guint lineno);
+void gb_source_change_monitor_reset (GbSourceChangeMonitor *monitor);
+
+G_END_DECLS
+
+#endif /* GB_SOURCE_CHANGE_MONITOR_H */
diff --git a/src/gnome-builder.mk b/src/gnome-builder.mk
index a6d9bd0..332d6b7 100644
--- a/src/gnome-builder.mk
+++ b/src/gnome-builder.mk
@@ -28,8 +28,12 @@ gnome_builder_SOURCES = \
src/editor/gb-editor-workspace.c \
src/editor/gb-editor-workspace.h \
src/editor/gb-editor-workspace-private.h \
+ src/editor/gb-source-change-monitor.c \
+ src/editor/gb-source-change-monitor.h \
src/editor/gb-source-formatter.c \
src/editor/gb-source-formatter.h \
+ src/editor/gb-source-change-gutter-renderer.c \
+ src/editor/gb-source-change-gutter-renderer.h \
src/editor/gb-source-search-highlighter.h \
src/editor/gb-source-search-highlighter.c \
src/markdown/gs-markdown.c \
diff --git a/src/resources/ui/gb-editor-tab.ui b/src/resources/ui/gb-editor-tab.ui
index aaa3ca3..3ea644e 100644
--- a/src/resources/ui/gb-editor-tab.ui
+++ b/src/resources/ui/gb-editor-tab.ui
@@ -124,6 +124,9 @@
</object>
<object class="GbEditorDocument" id="document">
</object>
+ <object class="GbSourceChangeMonitor" id="change_monitor">
+ <property name="buffer">document</property>
+ </object>
<object class="GbSourceSnippetCompletionProvider" id="snippets_provider">
<property name="source-view">source_view</property>
</object>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]