Commit 7b9b1d40 authored by Christian Hergert's avatar Christian Hergert

libide: start on IdeSourceView

There is a lot to move down here, but getting it into libide will generally
make things a lot nicer for scripting and consuming libide.

Other todo's involve moving snippets down.

Emacs/Vim mode are a tough choice. I haven't decided where that should
go yet.

Eventually, we should be able to make a very basic example using the
test application included. Once that is done, things are a candidate to
merge with Builder UI layers.
parent 6dd9d63e
......@@ -42,4 +42,5 @@ test-c-parse-helper
test-ide-back-forward-list
test-ide-buffer-manager
test-ide-context
test-ide-source-view
test-navigation-list
......@@ -46,6 +46,8 @@ libide_1_0_la_public_sources = \
libide/ide-buffer-manager.h \
libide/ide-buffer.c \
libide/ide-buffer.h \
libide/ide-source-view.c \
libide/ide-source-view.h \
libide/ide-build-result.c \
libide/ide-build-result.h \
libide/ide-build-system.c \
......@@ -185,6 +187,8 @@ libide_1_0_la_SOURCES = \
libide/ide-internal.h \
libide/tasks/ide-load-directory-task.c \
libide/tasks/ide-load-directory-task.h \
libide/util/ide-pango.c \
libide/util/ide-pango.h \
$(NULL)
libide_1_0_la_includes = \
......@@ -204,6 +208,7 @@ libide_1_0_la_includes = \
-I$(top_srcdir)/libide/pygobject \
-I$(top_srcdir)/libide/python \
-I$(top_srcdir)/libide/tasks \
-I$(top_srcdir)/libide/util \
-I$(top_srcdir)/libide/xml \
$(CLANG_CFLAGS) \
$(NULL)
......@@ -235,6 +240,7 @@ libide_1_0_la_LIBADD = \
$(CLANG_LDFLAGS) \
$(LIBIDE_LIBS) \
-lclang \
-lm \
libeditorconfig.la \
$(NULL)
......
/* ide-source-view.c
*
* Copyright (C) 2015 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 3 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 "ide-source-view"
#include <glib/gi18n.h>
#include "ide-buffer.h"
#include "ide-file.h"
#include "ide-file-settings.h"
#include "ide-highlighter.h"
#include "ide-indenter.h"
#include "ide-language.h"
#include "ide-pango.h"
#include "ide-source-view.h"
#define DEFAULT_FONT_DESC "Monospace 11"
typedef struct
{
IdeBuffer *buffer;
GtkCssProvider *css_provider;
PangoFontDescription *font_desc;
} IdeSourceViewPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (IdeSourceView, ide_source_view, GTK_SOURCE_TYPE_VIEW)
enum {
PROP_0,
PROP_FONT_NAME,
PROP_FONT_DESC,
LAST_PROP
};
static GParamSpec *gParamSpecs [LAST_PROP];
static void
ide_source_view__buffer_notify_file_cb (IdeSourceView *self,
GParamSpec *pspec,
IdeBuffer *buffer)
{
g_assert (IDE_IS_SOURCE_VIEW (self));
g_assert (IDE_IS_BUFFER (buffer));
}
static void
ide_source_view__buffer_notify_language_cb (IdeSourceView *self,
GParamSpec *pspec,
IdeBuffer *buffer)
{
g_assert (IDE_IS_SOURCE_VIEW (self));
g_assert (IDE_IS_BUFFER (buffer));
}
static void
ide_source_view__buffer_changed_cb (IdeSourceView *self,
IdeBuffer *buffer)
{
g_assert (IDE_IS_SOURCE_VIEW (self));
g_assert (IDE_IS_BUFFER (buffer));
}
static void
ide_source_view_rebuild_css (IdeSourceView *self)
{
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
if (!priv->css_provider)
{
GtkStyleContext *style_context;
priv->css_provider = gtk_css_provider_new ();
style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
gtk_style_context_add_provider (style_context,
GTK_STYLE_PROVIDER (priv->css_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
if (priv->font_desc)
{
g_autofree gchar *str = NULL;
g_autofree gchar *css = NULL;
str = ide_pango_font_description_to_css (priv->font_desc);
css = g_strdup_printf ("IdeSourceView { %s }", str ?: "");
gtk_css_provider_load_from_data (priv->css_provider, css, -1, NULL);
}
}
static void
ide_source_view_connect_buffer (IdeSourceView *self,
IdeBuffer *buffer)
{
g_assert (IDE_IS_SOURCE_VIEW (self));
g_assert (IDE_IS_BUFFER (buffer));
g_signal_connect_object (buffer,
"changed",
G_CALLBACK (ide_source_view__buffer_changed_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (buffer,
"notify::file",
G_CALLBACK (ide_source_view__buffer_notify_file_cb),
self,
G_CONNECT_SWAPPED);
g_signal_connect_object (buffer,
"notify::language",
G_CALLBACK (ide_source_view__buffer_notify_language_cb),
self,
G_CONNECT_SWAPPED);
}
static void
ide_source_view_disconnect_buffer (IdeSourceView *self,
IdeBuffer *buffer)
{
g_assert (IDE_IS_SOURCE_VIEW (self));
g_assert (IDE_IS_BUFFER (buffer));
g_signal_handlers_disconnect_by_func (buffer,
G_CALLBACK (ide_source_view__buffer_notify_file_cb),
self);
g_signal_handlers_disconnect_by_func (buffer,
G_CALLBACK (ide_source_view__buffer_notify_language_cb),
self);
}
static void
ide_source_view_notify_buffer (IdeSourceView *self,
GParamSpec *pspec,
gpointer user_data)
{
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
GtkTextBuffer *buffer;
g_assert (IDE_IS_SOURCE_VIEW (self));
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
if (priv->buffer != (IdeBuffer *)buffer)
{
if (priv->buffer != NULL)
{
ide_source_view_disconnect_buffer (self, priv->buffer);
g_clear_object (&priv->buffer);
}
/*
* Only enable IdeSourceView features if this is an IdeBuffer.
* Ignore for GtkSourceBuffer, and GtkTextBuffer.
*/
if (IDE_IS_BUFFER (buffer))
{
priv->buffer = g_object_ref (buffer);
ide_source_view_connect_buffer (self, priv->buffer);
}
}
}
static void
ide_source_view_dispose (GObject *object)
{
IdeSourceView *self = (IdeSourceView *)object;
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
if (priv->buffer)
{
ide_source_view_disconnect_buffer (self, priv->buffer);
g_clear_object (&priv->buffer);
}
g_clear_pointer (&priv->font_desc, pango_font_description_free);
G_OBJECT_CLASS (ide_source_view_parent_class)->dispose (object);
}
static void
ide_source_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
IdeSourceView *self = IDE_SOURCE_VIEW (object);
switch (prop_id)
{
case PROP_FONT_DESC:
g_value_set_boxed (value, ide_source_view_get_font_desc (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_source_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
IdeSourceView *self = IDE_SOURCE_VIEW (object);
switch (prop_id)
{
case PROP_FONT_NAME:
ide_source_view_set_font_name (self, g_value_get_string (value));
break;
case PROP_FONT_DESC:
ide_source_view_set_font_desc (self, g_value_get_boxed (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
ide_source_view_class_init (IdeSourceViewClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = ide_source_view_dispose;
object_class->get_property = ide_source_view_get_property;
object_class->set_property = ide_source_view_set_property;
gParamSpecs [PROP_FONT_DESC] =
g_param_spec_boxed ("font-desc",
_("Font Description"),
_("The Pango font description to use for rendering source."),
PANGO_TYPE_FONT_DESCRIPTION,
(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FONT_DESC,
gParamSpecs [PROP_FONT_DESC]);
gParamSpecs [PROP_FONT_NAME] =
g_param_spec_string ("font-name",
_("Font Name"),
_("The pango font name ot use for rendering source."),
"Monospace",
(G_PARAM_WRITABLE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (object_class, PROP_FONT_NAME,
gParamSpecs [PROP_FONT_NAME]);
}
static void
ide_source_view_init (IdeSourceView *self)
{
g_signal_connect (self, "notify::buffer", G_CALLBACK (ide_source_view_notify_buffer), NULL);
}
const PangoFontDescription *
ide_source_view_get_font_desc (IdeSourceView *self)
{
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
g_return_val_if_fail (IDE_IS_SOURCE_VIEW (self), NULL);
return priv->font_desc;
}
void
ide_source_view_set_font_desc (IdeSourceView *self,
const PangoFontDescription *font_desc)
{
IdeSourceViewPrivate *priv = ide_source_view_get_instance_private (self);
g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
if (font_desc != priv->font_desc)
{
g_clear_pointer (&priv->font_desc, pango_font_description_free);
if (font_desc)
priv->font_desc = pango_font_description_copy (font_desc);
else
priv->font_desc = pango_font_description_from_string (DEFAULT_FONT_DESC);
ide_source_view_rebuild_css (self);
}
}
void
ide_source_view_set_font_name (IdeSourceView *self,
const gchar *font_name)
{
PangoFontDescription *font_desc = NULL;
g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
if (font_name)
font_desc = pango_font_description_from_string (font_name);
ide_source_view_set_font_desc (self, font_desc);
if (font_desc)
pango_font_description_free (font_desc);
}
/* ide-source-view.h
*
* Copyright (C) 2015 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 3 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 IDE_SOURCE_VIEW_H
#define IDE_SOURCE_VIEW_H
#include <gtksourceview/gtksource.h>
G_BEGIN_DECLS
#define IDE_TYPE_SOURCE_VIEW (ide_source_view_get_type())
#define IDE_SOURCE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_SOURCE_VIEW, IdeSourceView))
#define IDE_SOURCE_VIEW_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_SOURCE_VIEW, IdeSourceView const))
#define IDE_SOURCE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDE_TYPE_SOURCE_VIEW, IdeSourceViewClass))
#define IDE_IS_SOURCE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_SOURCE_VIEW))
#define IDE_IS_SOURCE_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDE_TYPE_SOURCE_VIEW))
#define IDE_SOURCE_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDE_TYPE_SOURCE_VIEW, IdeSourceViewClass))
typedef struct _IdeSourceView IdeSourceView;
typedef struct _IdeSourceViewClass IdeSourceViewClass;
struct _IdeSourceView
{
GtkSourceView parent;
};
struct _IdeSourceViewClass
{
GtkSourceViewClass parent_class;
};
GType ide_source_view_get_type (void);
void ide_source_view_set_font_name (IdeSourceView *self,
const gchar *font_name);
const PangoFontDescription *ide_source_view_get_font_desc (IdeSourceView *self);
void ide_source_view_set_font_desc (IdeSourceView *self,
const PangoFontDescription *font_desc);
G_END_DECLS
#endif /* IDE_SOURCE_VIEW_H */
......@@ -67,6 +67,7 @@ G_BEGIN_DECLS
#include "ide-service.h"
#include "ide-source-location.h"
#include "ide-source-range.h"
#include "ide-source-view.h"
#include "ide-symbol-resolver.h"
#include "ide-symbol.h"
#include "ide-target.h"
......
/* ide-pango.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/gstdio.h>
#include <math.h>
#include "ide-pango.h"
#define FONT_FAMILY "font-family"
#define FONT_VARIANT "font-variant"
#define FONT_STRETCH "font-stretch"
#define FONT_WEIGHT "font-weight"
#define FONT_SIZE "font-size"
gchar *
ide_pango_font_description_to_css (const PangoFontDescription *font_desc)
{
PangoFontMask mask;
GString *str;
#define ADD_KEYVAL(key,fmt) \
g_string_append(str,key":"fmt";")
#define ADD_KEYVAL_PRINTF(key,fmt,...) \
g_string_append_printf(str,key":"fmt";", __VA_ARGS__)
g_return_val_if_fail (font_desc, NULL);
str = g_string_new (NULL);
mask = pango_font_description_get_set_fields (font_desc);
if ((mask & PANGO_FONT_MASK_FAMILY) != 0)
{
const gchar *family;
family = pango_font_description_get_family (font_desc);
ADD_KEYVAL_PRINTF (FONT_FAMILY, "\"%s\"", family);
}
if ((mask & PANGO_FONT_MASK_STYLE) != 0)
{
PangoVariant variant;
variant = pango_font_description_get_variant (font_desc);
switch (variant)
{
case PANGO_VARIANT_NORMAL:
ADD_KEYVAL (FONT_VARIANT, "normal");
break;
case PANGO_VARIANT_SMALL_CAPS:
ADD_KEYVAL (FONT_VARIANT, "small-caps");
break;
default:
break;
}
}
if ((mask & PANGO_FONT_MASK_WEIGHT))
{
gint weight;
weight = pango_font_description_get_weight (font_desc);
/*
* WORKAROUND:
*
* font-weight with numbers does not appear to be working as expected
* right now. So for the common (bold/normal), let's just use the string
* and let gtk warn for the other values, which shouldn't really be
* used for this.
*/
switch (weight)
{
case PANGO_WEIGHT_SEMILIGHT:
/*
* 350 is not actually a valid css font-weight, so we will just round
* up to 400.
*/
case PANGO_WEIGHT_NORMAL:
ADD_KEYVAL (FONT_WEIGHT, "normal");
break;
case PANGO_WEIGHT_BOLD:
ADD_KEYVAL (FONT_WEIGHT, "bold");
break;
case PANGO_WEIGHT_THIN:
case PANGO_WEIGHT_ULTRALIGHT:
case PANGO_WEIGHT_LIGHT:
case PANGO_WEIGHT_BOOK:
case PANGO_WEIGHT_MEDIUM:
case PANGO_WEIGHT_SEMIBOLD:
case PANGO_WEIGHT_ULTRABOLD:
case PANGO_WEIGHT_HEAVY:
case PANGO_WEIGHT_ULTRAHEAVY:
default:
/* round to nearest hundred */
weight = round (weight / 100.0) * 100;
ADD_KEYVAL_PRINTF ("font-weight", "%d", weight);
break;
}
}
if ((mask & PANGO_FONT_MASK_STRETCH))
{
switch (pango_font_description_get_stretch (font_desc))
{
case PANGO_STRETCH_ULTRA_CONDENSED:
ADD_KEYVAL (FONT_STRETCH, "untra-condensed");
break;
case PANGO_STRETCH_EXTRA_CONDENSED:
ADD_KEYVAL (FONT_STRETCH, "extra-condensed");
break;
case PANGO_STRETCH_CONDENSED:
ADD_KEYVAL (FONT_STRETCH, "condensed");
break;
case PANGO_STRETCH_SEMI_CONDENSED:
ADD_KEYVAL (FONT_STRETCH, "semi-condensed");
break;
case PANGO_STRETCH_NORMAL:
ADD_KEYVAL (FONT_STRETCH, "normal");
break;
case PANGO_STRETCH_SEMI_EXPANDED:
ADD_KEYVAL (FONT_STRETCH, "semi-expanded");
break;
case PANGO_STRETCH_EXPANDED:
ADD_KEYVAL (FONT_STRETCH, "expanded");
break;
case PANGO_STRETCH_EXTRA_EXPANDED:
ADD_KEYVAL (FONT_STRETCH, "extra-expanded");
break;
case PANGO_STRETCH_ULTRA_EXPANDED:
ADD_KEYVAL (FONT_STRETCH, "untra-expanded");
break;
default:
break;
}
}
if ((mask & PANGO_FONT_MASK_SIZE))
{
gint font_size;
font_size = pango_font_description_get_size (font_desc) / PANGO_SCALE;
ADD_KEYVAL_PRINTF ("font-size", "%dpx", font_size);
}
return g_string_free (str, FALSE);
#undef ADD_KEYVAL
#undef ADD_KEYVAL_PRINTF
}
/* ide-pango.h
*
* 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/>.
*/
#ifndef IDE_PANGO_H
#define IDE_PANGO_H
#include <pango/pango.h>
G_BEGIN_DECLS
gchar *ide_pango_font_description_to_css (const PangoFontDescription *font_desc);
G_END_DECLS
#endif /* GB_PANGO_H */
/* test-ide-source-view.c
*
* Copyright (C) 2015 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 <ide.h>
static IdeContext *gContext;
static void
load_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IdeBufferManager *buffer_manager = (IdeBufferManager *)object;
g_autoptr(IdeBuffer) buffer = NULL;
g_autoptr(GError) error = NULL;
IdeSourceView *source_view = user_data;
g_assert (IDE_IS_SOURCE_VIEW (source_view));
buffer = ide_buffer_manager_load_file_finish (buffer_manager, result, &error);
if (!buffer)
{
g_warning ("%s", error->message);
gtk_main_quit ();
return;
}
gtk_text_view_set_buffer (GTK_TEXT_VIEW (source_view), GTK_TEXT_BUFFER (buffer));
gtk_widget_set_sensitive (GTK_WIDGET (source_view), TRUE);
gtk_widget_grab_focus (GTK_WIDGET (source_view));
}
static void
context_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
IdeSourceView *source_view = user_data;
g_autoptr(IdeContext) context = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(IdeFile) file = NULL;
IdeProject *project;
IdeBufferManager *buffer_manager;
g_assert (IDE_IS_SOURCE_VIEW (source_view));
if (!(context = ide_context_new_finish (result, &error)))
{
g_warning ("%s", error->message);
gtk_main_quit ();
return;
}
project = ide_context_get_project (context);
file = ide_project_get_file_for_path (project, "test.c");
buffer_manager = ide_context_get_buffer_manager (context);
ide_buffer_manager_load_file_async (buffer_manager, file, FALSE,
NULL, NULL, load_cb, source_view);
gContext = g_object_ref (context);
}
static gboolean
cancel_ops (GCancellable *cancellable)
{
g_cancellable_cancel (cancellable);
return FALSE;
}
gint
main (gint argc,
gchar *argv[])
{
GFile *project_file;
GtkScrolledWindow *scroller;
IdeSourceView *source_view;
GtkWindow *window;
GCancellable *cancellable;
ide_set_program_name ("gnome-builder");
gtk_init (&argc, &argv);
cancellable = g_cancellable_new ();
window = g_object_new (GTK_TYPE_WINDOW,
"title", "IdeSourceView Test",
"default-width", 600,
"default-height", 600,
NULL);
scroller = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (scroller));
source_view = g_object_new (IDE_TYPE_SOURCE_VIEW,
"sensitive", FALSE,
"show-line-numbers", TRUE,
"visible", TRUE,
NULL);
gtk_container_add (GTK_CONTAINER (scroller), GTK_WIDGET (source_view));