/** * @file * "Format" expression-related functions of the \e GtkExperimentTranscript * widget */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "gtk-experiment-transcript-private.h" #define FORMAT_REGEX_COMPILE_FLAGS (G_REGEX_CASELESS) #define FORMAT_REGEX_MATCH_FLAGS (0) static gboolean gtk_experiment_transcript_parse_format(GtkExperimentTranscriptFormat *fmt, const gchar *str) { PangoAttrIterator *iter; gchar *pattern, pattern_captures[255], *p; gint capture_count = 0; if (!pango_parse_markup(str, -1, 0, &fmt->attribs, &pattern, NULL, NULL)) return TRUE; p = pattern_captures; iter = pango_attr_list_get_iterator(fmt->attribs); do { gint start, end; pango_attr_iterator_range(iter, &start, &end); if (end < G_MAXINT) { *p++ = '('; strncpy(p, pattern + start, end - start); p += end - start; *p++ = ')'; capture_count++; } } while (pango_attr_iterator_next(iter)); pango_attr_iterator_destroy(iter); *p = '\0'; g_free(pattern); fmt->regexp = g_regex_new(pattern_captures, FORMAT_REGEX_COMPILE_FLAGS, 0, NULL); if (fmt->regexp == NULL || g_regex_get_capture_count(fmt->regexp) != capture_count) { gtk_experiment_transcript_free_format(fmt); fmt->regexp = NULL; fmt->attribs = NULL; return TRUE; } return FALSE; } /** @private */ void gtk_experiment_transcript_apply_format(GtkExperimentTranscriptFormat *fmt, const gchar *text, PangoAttrList *attrib_list) { GMatchInfo *match_info; if (fmt->regexp == NULL || fmt->attribs == NULL) return; g_regex_match(fmt->regexp, text, FORMAT_REGEX_MATCH_FLAGS, &match_info); while (g_match_info_matches(match_info)) { PangoAttrIterator *iter; gint match_num = 1; iter = pango_attr_list_get_iterator(fmt->attribs); do { gint start, end; pango_attr_iterator_range(iter, &start, &end); if (end == G_MAXINT) continue; start = end = -1; g_match_info_fetch_pos(match_info, match_num, &start, &end); if (start >= 0 && end >= 0) { GSList *attribs; attribs = pango_attr_iterator_get_attrs(iter); for (GSList *cur = attribs; cur != NULL; cur = cur->next) { PangoAttribute *attrib; attrib = pango_attribute_copy((PangoAttribute *)cur->data); attrib->start_index = (guint)start; attrib->end_index = (guint)end; pango_attr_list_change(attrib_list, attrib); } g_slist_free(attribs); } match_num++; } while (pango_attr_iterator_next(iter)); pango_attr_iterator_destroy(iter); g_match_info_next(match_info, NULL); } g_match_info_free(match_info); } /** @private */ void gtk_experiment_transcript_free_formats(GSList *formats) { for (GSList *cur = formats; cur != NULL; cur = cur->next) { GtkExperimentTranscriptFormat *fmt = (GtkExperimentTranscriptFormat *)cur->data; gtk_experiment_transcript_free_format(fmt); g_free(fmt); } g_slist_free(formats); } /* * API */ /** @todo report errors using GError */ gboolean gtk_experiment_transcript_load_formats(GtkExperimentTranscript *trans, const gchar *filename) { FILE *file; gchar buf[255]; gboolean res = TRUE; gtk_experiment_transcript_free_formats(trans->priv->formats); trans->priv->formats = NULL; if (filename == NULL || !*filename) { res = FALSE; goto redraw; } if ((file = g_fopen(filename, "r")) == NULL) goto redraw; while (fgets((char *)buf, sizeof(buf)-1, file) != NULL) { GtkExperimentTranscriptFormat *fmt; g_strchug(buf); switch (*buf) { case '#': case '\r': case '\n': case '\0': continue; } fmt = g_new(GtkExperimentTranscriptFormat, 1); if (gtk_experiment_transcript_parse_format(fmt, buf)) { g_free(fmt); fclose(file); gtk_experiment_transcript_free_formats(trans->priv->formats); trans->priv->formats = NULL; goto redraw; } trans->priv->formats = g_slist_prepend(trans->priv->formats, fmt); } trans->priv->formats = g_slist_reverse(trans->priv->formats); fclose(file); res = FALSE; redraw: gtk_experiment_transcript_text_layer_redraw(trans); return res; } gboolean gtk_experiment_transcript_set_interactive_format(GtkExperimentTranscript *trans, const gchar *format_str, gboolean with_markup) { GtkExperimentTranscriptFormat *fmt = &trans->priv->interactive_format; gchar *pattern; PangoAttribute *attrib; gboolean res = TRUE; gtk_experiment_transcript_free_format(fmt); fmt->regexp = NULL; fmt->attribs = NULL; if (format_str == NULL || !*format_str) { res = FALSE; goto redraw; } if (with_markup) { res = gtk_experiment_transcript_parse_format(fmt, format_str); goto redraw; } /* else if (!with_markup) */ fmt->attribs = pango_attr_list_new(); if (fmt->attribs == NULL) goto redraw; if (trans->interactive_format.default_font != NULL) { attrib = pango_attr_font_desc_new(trans->interactive_format.default_font); attrib->end_index = 1; pango_attr_list_insert(fmt->attribs, attrib); } if (trans->interactive_format.default_text_color != NULL) { GdkColor *color = trans->interactive_format.default_text_color; attrib = pango_attr_foreground_new(color->red, color->green, color->blue); attrib->end_index = 1; pango_attr_list_insert(fmt->attribs, attrib); } if (trans->interactive_format.default_bg_color != NULL) { GdkColor *color = trans->interactive_format.default_bg_color; attrib = pango_attr_background_new(color->red, color->green, color->blue); attrib->end_index = 1; pango_attr_list_insert(fmt->attribs, attrib); } pattern = g_strconcat("(", format_str, ")", NULL); fmt->regexp = g_regex_new(pattern, FORMAT_REGEX_COMPILE_FLAGS, 0, NULL); g_free(pattern); if (fmt->regexp == NULL || g_regex_get_capture_count(fmt->regexp) != 1) { gtk_experiment_transcript_free_format(fmt); fmt->regexp = NULL; fmt->attribs = NULL; goto redraw; } res = FALSE; redraw: gtk_experiment_transcript_text_layer_redraw(trans); return res; }