/* * Copyright (C) 2012-2021 Robin Haberkorn * * 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 . */ requires 2.0.20 %ctop{ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sciteco.h" #include "string-utils.h" #define GDK_TO_PANGO_COLOR(X) ((guint16)((X) * G_MAXUINT16)) %} %h{ #include %} class Teco:Gtk:Label from Gtk:Label { private PangoColor fg; private guint16 fg_alpha; private PangoColor bg; private guint16 bg_alpha; private teco_string_t string destroy { teco_string_clear(&VAR); }; override (Gtk:Widget) void style_updated(Gtk:Widget *widget) { Self *self = SELF(widget); PARENT_HANDLER(widget); GtkStyleContext *style = gtk_widget_get_style_context(widget); GdkRGBA normal_color; gtk_style_context_get_color(style, GTK_STATE_NORMAL, &normal_color); selfp->bg.red = GDK_TO_PANGO_COLOR(normal_color.red); selfp->bg.green = GDK_TO_PANGO_COLOR(normal_color.green); selfp->bg.blue = GDK_TO_PANGO_COLOR(normal_color.blue); selfp->bg_alpha = GDK_TO_PANGO_COLOR(normal_color.alpha); /* * If Pango does not support transparent foregrounds, * it will at least use a high-contrast foreground. * * NOTE: It would be very hard to get an appropriate background * color even if Gtk supports it since the label itself may * not have one but one of its parents. * * FIXME: We may want to honour the background color, * so we can at least get decent reverse text when setting * the background color in the CSS. */ selfp->fg.red = normal_color.red > 0.5 ? 0 : G_MAXUINT16; selfp->fg.green = normal_color.green > 0.5 ? 0 : G_MAXUINT16; selfp->fg.blue = normal_color.blue > 0.5 ? 0 : G_MAXUINT16; /* try hard to get a transparent foreground anyway */ selfp->fg_alpha = 0; /* * The widget might be styled after the text has been set on it, * we must recreate the Pango attributes. */ if (selfp->string.len > 0) { PangoAttrList *attribs = NULL; g_autofree gchar *plaintext = NULL; self_parse_string(selfp->string.data, selfp->string.len, &selfp->fg, selfp->fg_alpha, &selfp->bg, selfp->bg_alpha, &attribs, &plaintext); gtk_label_set_attributes(GTK_LABEL(self), attribs); pango_attr_list_unref(attribs); g_assert(!g_strcmp0(plaintext, gtk_label_get_text(GTK_LABEL(self)))); } } public GtkWidget * new(const gchar *str, gssize len) { Self *widget = GET_NEW; self_set_text(widget, str, len); return GTK_WIDGET(widget); } private void add_highlight_attribs(Pango:Attr:List *attribs, Pango:Color *fg, guint16 fg_alpha, Pango:Color *bg, guint16 bg_alpha, guint index, gsize len) { PangoAttribute *attr; /* * NOTE: Transparent foreground do not seem to work, * even in Pango v1.38. * Perhaps, this has been fixed in later versions. */ #if PANGO_VERSION_CHECK(1,38,0) attr = pango_attr_foreground_alpha_new(fg_alpha); attr->start_index = index; attr->end_index = index + len; pango_attr_list_insert(attribs, attr); attr = pango_attr_background_alpha_new(bg_alpha); attr->start_index = index; attr->end_index = index + len; pango_attr_list_insert(attribs, attr); #endif attr = pango_attr_foreground_new(fg->red, fg->green, fg->blue); attr->start_index = index; attr->end_index = index + len; pango_attr_list_insert(attribs, attr); attr = pango_attr_background_new(bg->red, bg->green, bg->blue); attr->start_index = index; attr->end_index = index + len; pango_attr_list_insert(attribs, attr); } public void parse_string(const gchar *str, gssize len, Pango:Color *fg, guint16 fg_alpha, Pango:Color *bg, guint16 bg_alpha, Pango:Attr:List **attribs, gchar **text) { if (len < 0) len = strlen(str); /* * Approximate size of unformatted text. */ gsize text_len = 1; /* for trailing 0 */ for (gint i = 0; i < len; i++) text_len += TECO_IS_CTL(str[i]) ? 3 : 1; *attribs = pango_attr_list_new(); *text = g_malloc(text_len); gint index = 0; while (len > 0) { /* * NOTE: This mapping is similar to * teco_view_set_presentations() */ switch (*str) { case '\e': self_add_highlight_attribs(*attribs, fg, fg_alpha, bg, bg_alpha, index, 1); (*text)[index++] = '$'; break; case '\r': self_add_highlight_attribs(*attribs, fg, fg_alpha, bg, bg_alpha, index, 2); (*text)[index++] = 'C'; (*text)[index++] = 'R'; break; case '\n': self_add_highlight_attribs(*attribs, fg, fg_alpha, bg, bg_alpha, index, 2); (*text)[index++] = 'L'; (*text)[index++] = 'F'; break; case '\t': self_add_highlight_attribs(*attribs, fg, fg_alpha, bg, bg_alpha, index, 3); (*text)[index++] = 'T'; (*text)[index++] = 'A'; (*text)[index++] = 'B'; break; default: if (TECO_IS_CTL(*str)) { self_add_highlight_attribs(*attribs, fg, fg_alpha, bg, bg_alpha, index, 2); (*text)[index++] = '^'; (*text)[index++] = TECO_CTL_ECHO(*str); } else { (*text)[index++] = *str; } break; } str++; len--; } /* null-terminate generated text */ (*text)[index] = '\0'; } public void set_text(self, const gchar *str, gssize len) { teco_string_clear(&selfp->string); teco_string_init(&selfp->string, str, len < 0 ? strlen(str) : len); g_autofree gchar *plaintext = NULL; if (selfp->string.len > 0) { PangoAttrList *attribs = NULL; self_parse_string(selfp->string.data, selfp->string.len, &selfp->fg, selfp->fg_alpha, &selfp->bg, selfp->bg_alpha, &attribs, &plaintext); gtk_label_set_attributes(GTK_LABEL(self), attribs); pango_attr_list_unref(attribs); } gtk_label_set_text(GTK_LABEL(self), plaintext); } }