Logo Search packages:      
Sourcecode: gretl version File versions  Download package

gtksourcetag.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- 
 *  gtksourcetag.c
 *
 *  Copyright (C) 2001
 *  Mikael Hermansson<tyan@linux.se>
 *  Chris Phelps <chicane@reninet.com>
 *  Copyright (C) 2003 - Paolo Maggi <paolo.maggi@polito.it>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Library General Public License as published by
 *  the Free Software Foundation; either version 2 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>

#include "gtksourceview-i18n.h"
#include "gtksourcetag.h"
#include "gtksourcetag-private.h"

static GtkTextTagClass          *parent_class = NULL;
static GtkSourceTagClass      *parent_syntax_class  = NULL;
static GtkSourceTagClass      *parent_pattern_class = NULL;

static void        gtk_source_tag_init          (GtkSourceTag       *text_tag);
static void        gtk_source_tag_class_init    (GtkSourceTagClass  *text_tag);
static void        gtk_source_tag_finalize      (GObject            *object);

static void        gtk_syntax_tag_init          (GtkSyntaxTag       *text_tag);
static void        gtk_syntax_tag_class_init    (GtkSyntaxTagClass  *text_tag);
static void        gtk_syntax_tag_finalize      (GObject            *object);

static void        gtk_pattern_tag_init         (GtkPatternTag      *text_tag);
static void        gtk_pattern_tag_class_init   (GtkPatternTagClass *text_tag);
static void        gtk_pattern_tag_finalize     (GObject            *object);

static void              gtk_source_tag_set_property  (GObject            *object,
                                           guint               prop_id,
                                           const GValue       *value,
                                           GParamSpec         *pspec);
static void              gtk_source_tag_get_property  (GObject            *object,
                                           guint               prop_id,
                                           GValue             *value,
                                           GParamSpec         *pspec);

enum {
      PROP_0,
      PROP_ID,
      PROP_TAG_STYLE
};

/* Source tag */

GType
gtk_source_tag_get_type (void)
{
      static GType our_type = 0;

      if (our_type == 0) {
            static const GTypeInfo our_info = {
                  sizeof (GtkSourceTagClass),
                  (GBaseInitFunc) NULL,
                  (GBaseFinalizeFunc) NULL,
                  (GClassInitFunc) gtk_source_tag_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof (GtkSourceTag),
                  0,    /* n_preallocs */
                  (GInstanceInitFunc) gtk_source_tag_init
            };

            our_type =
                g_type_register_static (GTK_TYPE_TEXT_TAG,
                                  "GtkSourceTag", &our_info, 0);
      }

      return our_type;
}

static void
gtk_source_tag_class_init (GtkSourceTagClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      parent_class = g_type_class_peek_parent (klass);
      object_class->finalize  = gtk_source_tag_finalize;

      object_class->set_property = gtk_source_tag_set_property;
      object_class->get_property = gtk_source_tag_get_property;
  
      /* Construct */
      g_object_class_install_property (object_class,
                                     PROP_ID,
                                     g_param_spec_string ("id",
                                                        _("Tag ID"),
                                                        _("ID used to refer to the source tag"),
                                                        NULL,
                                                        G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));

      g_object_class_install_property (object_class,
                                     PROP_TAG_STYLE,
                                     g_param_spec_boxed ("tag_style",
                                                       _("Tag style"),
                                                       _("The style associated to the source tag"),
                                                       GTK_TYPE_SOURCE_TAG_STYLE,
                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));

}

static void
gtk_source_tag_init (GtkSourceTag *text_tag)
{
      text_tag->id = NULL;
      text_tag->style = NULL;
}

static void
gtk_source_tag_finalize (GObject *object)
{
      GtkSourceTag *tag;

      tag = GTK_SOURCE_TAG (object);
      
      g_free (tag->style);
      g_free (tag->id);
      
      G_OBJECT_CLASS (parent_class)->finalize (object);
}

GtkSourceTagStyle *
gtk_source_tag_get_style (GtkSourceTag *tag)
{
      g_return_val_if_fail (GTK_IS_SOURCE_TAG (tag), NULL);

      if (tag->style != NULL)
            return gtk_source_tag_style_copy (tag->style);
      else
            return NULL;
}

void 
gtk_source_tag_set_style (GtkSourceTag *tag, const GtkSourceTagStyle *style)
{
      GValue foreground = { 0, };
      GValue background = { 0, };

      g_return_if_fail (GTK_IS_SOURCE_TAG (tag));
      g_return_if_fail (style != NULL);

      /* Foreground color */
      g_value_init (&foreground, GDK_TYPE_COLOR);
      
      if ((style->mask & GTK_SOURCE_TAG_STYLE_USE_FOREGROUND) != 0)
            g_value_set_boxed (&foreground, &style->foreground);
      else
            g_value_set_boxed (&foreground, NULL);
      
      g_object_set_property (G_OBJECT (tag), "foreground_gdk", &foreground);

      /* Background color */
      g_value_init (&background, GDK_TYPE_COLOR);

      if ((style->mask & GTK_SOURCE_TAG_STYLE_USE_BACKGROUND) != 0)
            g_value_set_boxed (&background, &style->background);
      else
            g_value_set_boxed (&background, NULL);

      g_object_set_property (G_OBJECT (tag), "background_gdk", &background);
            
      g_object_set (G_OBJECT (tag), 
                  "style", style->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL,
                  "weight", style->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
                  "strikethrough", style->strikethrough,
                  "underline", style->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE,
                  NULL);

      g_free (tag->style);

      tag->style = g_new0 (GtkSourceTagStyle, 1);

      *tag->style = *style;
}


/* Syntax tag */

GType
gtk_syntax_tag_get_type (void)
{
      static GType our_type = 0;

      if (our_type == 0) {
            static const GTypeInfo our_info = {
                  sizeof (GtkSyntaxTagClass),
                  (GBaseInitFunc) NULL,
                  (GBaseFinalizeFunc) NULL,
                  (GClassInitFunc) gtk_syntax_tag_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof (GtkSyntaxTag),
                  0,    /* n_preallocs */
                  (GInstanceInitFunc) gtk_syntax_tag_init
            };

            our_type =
                g_type_register_static (GTK_TYPE_SOURCE_TAG,
                                  "GtkSyntaxTag", &our_info, 0);
      }

      return our_type;
}

static void
gtk_syntax_tag_class_init (GtkSyntaxTagClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      parent_syntax_class     = g_type_class_peek_parent (klass);
      object_class->finalize  = gtk_syntax_tag_finalize;
}

static void
gtk_syntax_tag_init (GtkSyntaxTag *text_tag)
{
}


GtkTextTag *
gtk_syntax_tag_new (const gchar *id,      
                const gchar *name, 
                const gchar *pattern_start,
                const gchar *pattern_end)
{
      GtkSyntaxTag *tag;

      g_return_val_if_fail (pattern_start != NULL, NULL);
      g_return_val_if_fail (pattern_end != NULL, NULL);

      tag = GTK_SYNTAX_TAG (g_object_new (GTK_TYPE_SYNTAX_TAG, 
                                  "id", id,
                                  "name", name,
                                  NULL));
      
      tag->start = g_strdup (pattern_start);

      tag->reg_start = gtk_source_regex_compile (pattern_start);
      if (tag->reg_start == NULL) {
            g_warning ("Regex syntax start pattern failed [%s]", pattern_start);
            g_object_unref (tag);
            return NULL;
      }
      
      tag->reg_end = gtk_source_regex_compile (pattern_end);
      if (tag->reg_end == NULL) {
            g_warning ("Regex syntax end pattern failed [%s]\n", pattern_end);
            g_object_unref (tag);
            return NULL;
      }

      return GTK_TEXT_TAG (tag);
}

static void
gtk_syntax_tag_finalize (GObject *object)
{
      GtkSyntaxTag *tag;

      tag = GTK_SYNTAX_TAG (object);
      
      g_free (tag->start);
      
      gtk_source_regex_destroy (tag->reg_start);
      gtk_source_regex_destroy (tag->reg_end);

      G_OBJECT_CLASS (parent_syntax_class)->finalize (object);
}


/* Pattern Tag */

GType
gtk_pattern_tag_get_type (void)
{
      static GType our_type = 0;

      if (our_type == 0) {
            static const GTypeInfo our_info = {
                  sizeof (GtkPatternTagClass),
                  (GBaseInitFunc) NULL,
                  (GBaseFinalizeFunc) NULL,
                  (GClassInitFunc) gtk_pattern_tag_class_init,
                  NULL, /* class_finalize */
                  NULL, /* class_data */
                  sizeof (GtkPatternTag),
                  0,    /* n_preallocs */
                  (GInstanceInitFunc) gtk_pattern_tag_init
            };

            our_type =
                g_type_register_static (GTK_TYPE_SOURCE_TAG,
                                  "GtkPatternTag", &our_info, 0);
      }
      return (our_type);
}

static void
gtk_pattern_tag_init (GtkPatternTag *text_tag)
{
}

static void
gtk_pattern_tag_class_init (GtkPatternTagClass *klass)
{
      GObjectClass *object_class = G_OBJECT_CLASS (klass);

      parent_pattern_class    = g_type_class_peek_parent (klass);
      object_class->finalize  = gtk_pattern_tag_finalize;
}

GtkTextTag *
gtk_pattern_tag_new (const gchar *id, 
                 const gchar *name, 
                 const gchar *pattern)
{
      GtkPatternTag *tag;

      g_return_val_if_fail (pattern != NULL, NULL);

      tag = GTK_PATTERN_TAG (g_object_new (GTK_TYPE_PATTERN_TAG, 
                                   "id", id,
                                   "name", name,
                                   NULL));
      
      tag->reg_pattern = gtk_source_regex_compile (pattern);
      if (tag->reg_pattern == NULL) {
            g_warning ("Regex pattern failed [%s]\n", pattern);
            g_object_unref (tag);
            return NULL;
      }

      return GTK_TEXT_TAG (tag);
}

static void
gtk_pattern_tag_finalize (GObject *object)
{
      GtkPatternTag *tag;

      tag = GTK_PATTERN_TAG (object);
      
      gtk_source_regex_destroy (tag->reg_pattern);
      
      G_OBJECT_CLASS (parent_pattern_class)->finalize (object);
}


static gchar *
case_insesitive_keyword (const gchar *keyword)
{
      GString *str;
      gint length;
      
      const gchar *cur;
      const gchar *end;

      g_return_val_if_fail (keyword != NULL, NULL);

      length = strlen (keyword);

      str = g_string_new ("");

      cur = keyword;
      end = keyword + length;
      
      while (cur != end) 
      {
            gunichar cur_char;
            cur_char = g_utf8_get_char (cur);
            
            if (((cur_char >= 'A') && (cur_char <= 'Z')) ||
                ((cur_char >= 'a') && (cur_char <= 'z')))
            {
                  gunichar cur_char_upper;
                        gunichar cur_char_lower;
      
                  cur_char_upper = g_unichar_toupper (cur_char);
                  cur_char_lower = g_unichar_tolower (cur_char);
            
                  g_string_append_c (str, '[');
                  g_string_append_unichar (str, cur_char_lower);
                  g_string_append_unichar (str, cur_char_upper);
                  g_string_append_c (str, ']');
            }
            else
                  g_string_append_unichar (str, cur_char);

            cur = g_utf8_next_char (cur);
      }
                  
      return g_string_free (str, FALSE);
}

GtkTextTag *
gtk_keyword_list_tag_new (const gchar  *id,
                    const gchar  *name, 
                    const GSList *keywords,
                    gboolean      case_sensitive,
                    gboolean      match_empty_string_at_beginning,
                    gboolean      match_empty_string_at_end,
                    const gchar  *beginning_regex,
                    const gchar  *end_regex)
{
      
      GtkTextTag *tag;
      GString *str;
      gint keyword_count;
      
      g_return_val_if_fail (keywords != NULL, NULL);

      str =  g_string_new ("");

      if (keywords != NULL)
      {
            if (match_empty_string_at_beginning)
                  g_string_append (str, "\\b");

            if (beginning_regex != NULL)
                  g_string_append (str, beginning_regex);

            g_string_append (str, "(");
      
            keyword_count = 0;
            /* Due to a bug in GNU libc regular expressions
             * implementation we can't have keyword lists of more
             * than 250 or so elements, so we truncate such a
             * list.  This is a temporary solution, as the correct
             * approach would be to generate multiple keyword
             * lists.  (See bug #110991) */

#define KEYWORD_LIMIT 250

            while (keywords != NULL && keyword_count < KEYWORD_LIMIT)
            {
                  gchar *k;
                  
                  if (case_sensitive)
                        k = (gchar*)keywords->data;
                  else
                        k = case_insesitive_keyword ((gchar*)keywords->data);
                  
                  g_string_append (str, k);
                  
                  if (!case_sensitive)
                        g_free (k);
                  
                  keywords = g_slist_next (keywords);
                  
                  keyword_count++;
                  
                  if (keywords != NULL && keyword_count < KEYWORD_LIMIT)
                        g_string_append (str, "|");
            }
            g_string_append (str, ")");
            
            if (keyword_count >= KEYWORD_LIMIT)
            {
                  g_warning ("Keyword list '%s' too long. Only the first %d "
                           "elements will be highlighted. See bug #110991 for "
                           "further details.", id, KEYWORD_LIMIT);
            }

            if (end_regex != NULL)
                  g_string_append (str, end_regex);
            
            if (match_empty_string_at_end)
                  g_string_append (str, "\\b");
      }

      tag = gtk_pattern_tag_new (id, name, str->str);

      g_string_free (str, TRUE);
      
      return tag;
}

GtkTextTag *
gtk_line_comment_tag_new (const gchar *id, 
                    const gchar *name, 
                    const gchar *pattern_start)
{
      g_return_val_if_fail (pattern_start != NULL, NULL);

      return gtk_syntax_tag_new (id, name, pattern_start, "\n");
}

GtkTextTag *
gtk_string_tag_new (const gchar    *id,
                const gchar    *name,
                  const gchar    *pattern_start,
                const gchar    *pattern_end,
                gboolean        end_at_line_end)
{
      g_return_val_if_fail (pattern_start != NULL, NULL);
      g_return_val_if_fail (pattern_end != NULL, NULL);

      if (!end_at_line_end)
            return gtk_syntax_tag_new (id, name, pattern_start, pattern_end);
      else
      {
            GtkTextTag *tag;
            gchar *end;
            
            end = g_strdup_printf ("%s|\n", pattern_end);

            tag = gtk_syntax_tag_new (id, name, pattern_start, end);

            g_free (end);

            return tag;
      }
}

static void
gtk_source_tag_set_property (GObject            *object,
                       guint               prop_id,
                       const GValue       *value,
                       GParamSpec         *pspec)
{
      GtkSourceTag *tag;

      g_return_if_fail (GTK_IS_SOURCE_TAG (object));

      tag = GTK_SOURCE_TAG (object);

      switch (prop_id)
      {
            case PROP_ID:
                  g_return_if_fail (tag->id == NULL);
                  tag->id = g_strdup (g_value_get_string (value));
                  break;

            case PROP_TAG_STYLE:
                  {
                        const GtkSourceTagStyle *style;

                        style = g_value_get_boxed (value);

                        if (style != NULL)
                              gtk_source_tag_set_style (tag, style);
                  }
                        
            default:
                  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      }
}

static void
gtk_source_tag_get_property (GObject      *object,
                             guint         prop_id,
                             GValue       *value,
                             GParamSpec   *pspec)
{
      GtkSourceTag *tag;

      g_return_if_fail (GTK_IS_SOURCE_TAG (object));

      tag = GTK_SOURCE_TAG (object);

      switch (prop_id)
      {
            case PROP_ID:
                  g_value_set_string (value, tag->id);
                  break;

            case PROP_TAG_STYLE:
                  {
                        GtkSourceTagStyle *style;
                        
                        style = gtk_source_tag_get_style (tag);

                        g_value_set_boxed (value, style);

                        if (style != NULL)
                              gtk_source_tag_style_free (style);
                        
                        break;
                  }
            default:
                  G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      }
}

gchar *
gtk_source_tag_get_id (GtkSourceTag *tag)
{
      g_return_val_if_fail (GTK_IS_SOURCE_TAG (tag), NULL);
      g_return_val_if_fail (tag->id != NULL, NULL);

      return g_strdup (tag->id);
}
      

/* GtkSourceTagStyle functions ------------- */

GType 
gtk_source_tag_style_get_type (void)
{
      static GType our_type = 0;

      if (!our_type)
            our_type = g_boxed_type_register_static (
                  "GtkSourceTagStyle",
                  (GBoxedCopyFunc) gtk_source_tag_style_copy,
                  (GBoxedFreeFunc) gtk_source_tag_style_free);

      return our_type;
} 

GtkSourceTagStyle *
gtk_source_tag_style_new (void)
{
      GtkSourceTagStyle *style;

      style = g_new0 (GtkSourceTagStyle, 1);

      return style;
}

GtkSourceTagStyle *
gtk_source_tag_style_copy (const GtkSourceTagStyle *style)
{
      GtkSourceTagStyle *new_style;

      g_return_val_if_fail (style != NULL, NULL);
      
      new_style = gtk_source_tag_style_new ();
      *new_style = *style;

      return new_style;
}

void 
gtk_source_tag_style_free (GtkSourceTagStyle *style)
{
      g_return_if_fail (style != NULL);
      
      g_free (style);
}



Generated by  Doxygen 1.6.0   Back to index