/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4; coding: utf-8 -*- * * moolang.c * * Copyright (C) 2004-2006 by Yevgen Muntyan * * 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 2 of the License, or * (at your option) any later version. * * See COPYING file that comes with this distribution. */ #define MOOEDIT_COMPILATION #include "mooedit/moolangmgr.h" #include "mooedit/moolang-rules.h" #include "mooedit/moolang-parser.h" #include "mooedit/moolang-strings.h" #include "mooedit/mooeditprefs.h" #include "mooutils/xdgmime/xdgmime.h" #include "mooutils/mooprefs.h" #include "mooutils/moomarshals.h" #include #define LANG_FILE_SUFFIX ".lang" #define LANG_FILE_SUFFIX_LEN 5 #define STYLES_FILE_SUFFIX ".styles" #define STYLES_FILE_SUFFIX_LEN 7 #define EXTENSION_SEPARATOR ";" /*****************************************************************************/ /* MooContext */ static MooContext* moo_context_new (MooLang *lang, const char *name, const char *style) { MooContext *ctx; g_return_val_if_fail (lang != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); ctx = g_new0 (MooContext, 1); ctx->lang = lang; ctx->name = g_strdup (name); ctx->style = g_strdup (style); ctx->rules = (MooRuleArray*) g_ptr_array_new (); return ctx; } static void moo_context_free (MooContext *ctx) { if (ctx) { guint i; g_free (ctx->name); g_free (ctx->style); for (i = 0; i < ctx->rules->len; ++i) moo_rule_free (ctx->rules->data[i]); g_ptr_array_free ((GPtrArray*) ctx->rules, TRUE); g_free (ctx); } } void moo_context_add_rule (MooContext *ctx, MooRule *rule) { g_return_if_fail (ctx != NULL && rule != NULL); g_return_if_fail (rule->context == NULL); rule->context = ctx; g_ptr_array_add ((GPtrArray*) ctx->rules, rule); } void moo_context_set_eol_stay (MooContext *ctx) { g_return_if_fail (ctx != NULL); ctx->line_end.type = MOO_CONTEXT_STAY; ctx->line_end.num = 0; } void moo_context_set_eol_pop (MooContext *ctx, guint num) { g_return_if_fail (ctx != NULL); g_return_if_fail (num > 0); ctx->line_end.type = MOO_CONTEXT_POP; ctx->line_end.num = num; } void moo_context_set_eol_switch (MooContext *ctx, MooContext *target) { g_return_if_fail (ctx != NULL && target != NULL); ctx->line_end.type = MOO_CONTEXT_SWITCH; ctx->line_end.ctx = target; } /*****************************************************************************/ /* MooLang */ static void lang_connect_scheme (MooLang *lang, MooTextStyleScheme *scheme); static void lang_disconnect_scheme (MooLang *lang, MooTextStyleScheme *scheme); static void style_cache_create (MooLang *lang) { g_return_if_fail (lang->style_cache == NULL); lang->style_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_destroy); } static void scheme_destroyed (MooLang *lang, MooTextStyleScheme *scheme) { g_hash_table_remove (lang->style_cache, scheme); } static void scheme_changed (MooTextStyleScheme *scheme, MooLang *lang) { g_hash_table_remove (lang->style_cache, scheme); lang_disconnect_scheme (lang, scheme); } static void disconnect_scheme (MooTextStyleScheme *scheme, G_GNUC_UNUSED GHashTable *scheme_cache, MooLang *lang) { lang_disconnect_scheme (lang, scheme); } static void style_cache_destroy (MooLang *lang) { g_hash_table_foreach (lang->style_cache, (GHFunc) disconnect_scheme, lang); g_hash_table_destroy (lang->style_cache); lang->style_cache = NULL; } static MooTextStyle* style_cache_lookup (MooLang *lang, const char *style_name, MooTextStyleScheme *scheme) { GHashTable *scheme_cache = g_hash_table_lookup (lang->style_cache, scheme); return scheme_cache ? g_hash_table_lookup (scheme_cache, style_name) : NULL; } static void style_cache_insert (MooLang *lang, const char *style_name, MooTextStyleScheme *scheme, MooTextStyle *style) { GHashTable *scheme_cache = g_hash_table_lookup (lang->style_cache, scheme); if (!scheme_cache) { lang_connect_scheme (lang, scheme); scheme_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) moo_text_style_free); g_hash_table_insert (lang->style_cache, scheme, scheme_cache); } g_hash_table_insert (scheme_cache, g_strdup (style_name), style); } static void lang_disconnect_scheme (MooLang *lang, MooTextStyleScheme *scheme) { g_signal_handlers_disconnect_by_func (scheme, (gpointer) scheme_changed, lang); g_object_weak_unref (G_OBJECT (scheme), (GWeakNotify) scheme_destroyed, lang); } static void lang_connect_scheme (MooLang *lang, MooTextStyleScheme *scheme) { g_signal_connect (scheme, "changed", G_CALLBACK (scheme_changed), lang); g_object_weak_ref (G_OBJECT (scheme), (GWeakNotify) scheme_destroyed, lang); } MooLang* moo_lang_new (MooLangMgr *mgr, const char *name, const char *section, const char *version, const char *author) { MooLang *lang; g_return_val_if_fail (MOO_IS_LANG_MGR (mgr) && name != NULL && name[0] != 0, NULL); g_return_val_if_fail (!g_hash_table_lookup (mgr->lang_names, name), NULL); lang = g_new0 (MooLang, 1); lang->mgr = mgr; lang->context_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); lang->contexts = (MooContextArray*) g_ptr_array_new (); lang->style_names = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); lang->styles = (MooTextStyleArray*) g_ptr_array_new (); style_cache_create (lang); lang->id = g_ascii_strdown (name, -1); lang->display_name = g_strdup (name); lang->section = section ? g_strdup (section) : g_strdup ("Others"); lang->version = version ? g_strdup (version) : g_strdup (""); lang->author = author ? g_strdup (author) : g_strdup (""); _moo_lang_mgr_add_lang (mgr, lang); return lang; } void moo_lang_add_style (MooLang *lang, const char *name, const MooTextStyle *style) { MooTextStyle *copy; g_return_if_fail (lang != NULL); g_return_if_fail (name != NULL); g_return_if_fail (!g_hash_table_lookup (lang->style_names, name)); if (style) copy = moo_text_style_copy (style); else copy = moo_text_style_new (NULL, NULL, NULL, 0, 0, 0, 0, 0, FALSE); g_ptr_array_add ((GPtrArray*) lang->styles, copy); g_hash_table_insert (lang->style_names, g_strdup (name), copy); } MooLang* moo_lang_ref (MooLang *lang) { g_return_val_if_fail (lang != NULL && lang->mgr != NULL, NULL); g_object_ref (lang->mgr); return lang; } void moo_lang_unref (MooLang *lang) { g_return_if_fail (lang != NULL && lang->mgr != NULL); g_object_unref (lang->mgr); } MooContext* moo_lang_add_context (MooLang *lang, const char *name, const char *style) { MooContext *ctx; g_return_val_if_fail (lang != NULL, NULL); g_return_val_if_fail (name != NULL && name[0] != 0, NULL); g_return_val_if_fail (!g_hash_table_lookup (lang->context_names, name), NULL); ctx = moo_context_new (lang, name, style); g_hash_table_insert (lang->context_names, g_strdup (name), ctx); g_ptr_array_add ((GPtrArray*) lang->contexts, ctx); return ctx; } MooContext* moo_lang_get_context (MooLang *lang, const char *ctx_name) { g_return_val_if_fail (lang != NULL, NULL); g_return_val_if_fail (ctx_name != NULL, NULL); return g_hash_table_lookup (lang->context_names, ctx_name); } MooContext* moo_lang_get_default_context (MooLang *lang) { g_return_val_if_fail (lang != NULL, NULL); g_return_val_if_fail (lang->contexts->len != 0, NULL); return lang->contexts->data[0]; } void _moo_lang_free (MooLang *lang) { if (lang) { guint i; g_hash_table_destroy (lang->context_names); for (i = 0; i < lang->contexts->len; ++i) moo_context_free (lang->contexts->data[i]); g_ptr_array_free ((GPtrArray*) lang->contexts, TRUE); g_hash_table_destroy (lang->style_names); style_cache_destroy (lang); for (i = 0; i < lang->styles->len; ++i) moo_text_style_free (lang->styles->data[i]); g_ptr_array_free ((GPtrArray*) lang->styles, TRUE); g_free (lang->id); g_free (lang->display_name); g_free (lang->section); g_free (lang->version); g_free (lang->author); g_free (lang->sample); g_free (lang->brackets); g_free (lang->line_comment); g_free (lang->block_comment_start); g_free (lang->block_comment_end); g_slist_foreach (lang->mime_types, (GFunc) g_free, NULL); g_slist_free (lang->mime_types); g_slist_foreach (lang->extensions, (GFunc) g_free, NULL); g_slist_free (lang->extensions); g_free (lang); } } GType moo_lang_get_type (void) { static GType type = 0; if (!type) type = g_boxed_type_register_static ("MooLang", (GBoxedCopyFunc) moo_lang_ref, (GBoxedFreeFunc) moo_lang_unref); return type; } static void set_tag_style (MooLang *lang, GtkTextTag *tag, const char *style_name, MooTextStyleScheme *scheme) { MooTextStyle *style = NULL; g_return_if_fail (lang != NULL && tag != NULL); if (!style_name) return; if (!scheme) scheme = moo_lang_mgr_get_active_scheme (lang->mgr); g_return_if_fail (MOO_IS_TEXT_STYLE_SCHEME (scheme)); style = style_cache_lookup (lang, style_name, scheme); if (!style) { style = moo_lang_mgr_get_style (lang->mgr, lang->id, style_name, scheme); g_return_if_fail (style != NULL); if (style->default_style) { MooTextStyle *def_style; def_style = moo_lang_mgr_get_style (lang->mgr, NULL, style->default_style, scheme); if (!def_style) { g_warning ("%s: invalid default style name '%s'", G_STRLOC, style->default_style); } else { moo_text_style_compose (def_style, style); moo_text_style_free (style); style = def_style; } } style_cache_insert (lang, style_name, scheme, style); } if (style->mask & MOO_TEXT_STYLE_FOREGROUND) g_object_set (tag, "foreground-gdk", &style->foreground, NULL); if (style->mask & MOO_TEXT_STYLE_BACKGROUND) g_object_set (tag, "background-gdk", &style->background, NULL); if (style->mask & MOO_TEXT_STYLE_BOLD) g_object_set (tag, "weight", style->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL); if (style->mask & MOO_TEXT_STYLE_ITALIC) g_object_set (tag, "style", style->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL); if (style->mask & MOO_TEXT_STYLE_UNDERLINE) g_object_set (tag, "underline", style->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE, NULL); if (style->mask & MOO_TEXT_STYLE_STRIKETHROUGH) g_object_set (tag, "strikethrough", (gboolean) (style->strikethrough ? TRUE : FALSE), NULL); } void _moo_lang_set_tag_style (MooLang *lang, GtkTextTag *tag, MooContext *ctx, MooRule *rule, MooTextStyleScheme *scheme) { g_return_if_fail (lang != NULL && GTK_IS_TEXT_TAG (tag)); g_return_if_fail (ctx != NULL && ctx->lang == lang); g_return_if_fail (!rule || rule->context == ctx); set_tag_style (lang, tag, ctx->style, scheme); if (rule) set_tag_style (lang, tag, rule->style, scheme); } void _moo_style_set_tag_style (const MooTextStyle *style, GtkTextTag *tag) { g_return_if_fail (style != NULL); g_return_if_fail (GTK_IS_TEXT_TAG (tag)); if (style->mask & MOO_TEXT_STYLE_BACKGROUND) g_object_set (tag, "background-gdk", &style->background, NULL); else g_object_set (tag, "background-set", FALSE, NULL); if (style->mask & MOO_TEXT_STYLE_FOREGROUND) g_object_set (tag, "foreground-gdk", &style->foreground, NULL); else g_object_set (tag, "foreground-set", FALSE, NULL); if (style->mask & MOO_TEXT_STYLE_BOLD) g_object_set (tag, "weight", style->bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL); else g_object_set (tag, "weight-set", FALSE, NULL); if (style->mask & MOO_TEXT_STYLE_ITALIC) g_object_set (tag, "style", style->italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL, NULL); else g_object_set (tag, "style-set", FALSE, NULL); if (style->mask & MOO_TEXT_STYLE_UNDERLINE) g_object_set (tag, "underline", style->underline ? PANGO_UNDERLINE_SINGLE : PANGO_UNDERLINE_NONE, NULL); else g_object_set (tag, "underline-set", FALSE, NULL); if (style->mask & MOO_TEXT_STYLE_STRIKETHROUGH) g_object_set (tag, "strikethrough", (gboolean) (style->strikethrough ? TRUE : FALSE), NULL); else g_object_set (tag, "strikethrough-set", FALSE, NULL); } void _moo_lang_erase_tag_style (GtkTextTag *tag) { g_return_if_fail (GTK_IS_TEXT_TAG (tag)); g_object_set (tag, "background-set", FALSE, "foreground-set", FALSE, "weight-set", FALSE, "style-set", FALSE, "underline-set", FALSE, "strikethrough-set", FALSE, NULL); }