Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-ui-utils"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <gtk/gtk.h>
9 : : #include <pango/pango.h>
10 : : #include <libvalent-core.h>
11 : :
12 : : #include "valent-ui-utils.h"
13 : : #include "valent-ui-utils-private.h"
14 : :
15 : :
16 : : // Andy Holmes
17 : : #define EMAIL_PATTERN "\\b(?:.+@.+\\.[a-z]{2,4}\\b)"
18 : :
19 : : // https://gist.github.com/gruber/8891611 (changed tld list to `[a-z]{2,4}`)
20 : : #define URL_PATTERN "\\b((?:https?:(?:/{1,3}|[a-z0-9%])|[a-z0-9.\\-]+[.](?:[a-z]{2,4})/)(?:[^\\s()<>{}\\[\\]]+|\\([^\\s()]*?\\([^\\s()]+\\)[^\\s()]*?\\)|\\([^\\s]+?\\))+(?:\\([^\\s()]*?\\([^\\s()]+\\)[^\\s()]*?\\)|\\([^\\s]+?\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’])|(?:(?<!@)[a-z0-9]+(?:[.\\-][a-z0-9]+)*[.](?:[a-z]{2,4})\\b/?(?!@)))"
21 : :
22 : : #define URI_FLAGS (G_REGEX_CASELESS | G_REGEX_MULTILINE | G_REGEX_NO_AUTO_CAPTURE | G_REGEX_OPTIMIZE)
23 : :
24 : : static GRegex *email_regex = NULL;
25 : : static GRegex *uri_regex = NULL;
26 : :
27 : : static gboolean
28 : 4 : valent_ui_replace_eval_uri (const GMatchInfo *info,
29 : : GString *result,
30 : : gpointer user_data)
31 : : {
32 : 8 : g_autofree char *text = NULL;
33 : 4 : g_autofree char *escaped = NULL;
34 : :
35 : 4 : text = g_match_info_fetch (info, 0);
36 : :
37 [ + + ]: 4 : if (g_uri_is_valid (text, G_URI_FLAGS_NONE, NULL))
38 : : {
39 : 1 : escaped = g_markup_printf_escaped ("<a href=\"%s\">%s</a>",
40 : : text, text);
41 : : }
42 [ - + ]: 3 : else if (g_regex_match (email_regex, text, 0, NULL))
43 : : {
44 : 0 : escaped = g_markup_printf_escaped ("<a href=\"mailto:%s\">%s</a>",
45 : : text, text);
46 : : }
47 : : else
48 : : {
49 : 3 : escaped = g_markup_printf_escaped ("<a href=\"https://%s\">%s</a>",
50 : : text, text);
51 : : }
52 : :
53 [ - + ]: 4 : g_string_append (result, escaped);
54 : :
55 : 4 : return FALSE;
56 : : }
57 : :
58 : : /**
59 : : * valent_string_to_markup:
60 : : * @text: (nullable): input text
61 : : *
62 : : * Add markup to text for recognized elements.
63 : : *
64 : : * This function currently scans for URLs and e-mail addresses, then amends each
65 : : * element with anchor tags (`<a>`).
66 : : *
67 : : * If @text is %NULL, this function will return %NULL.
68 : : *
69 : : * Returns: (transfer full) (nullable): a string of Pango markup
70 : : *
71 : : * Since: 1.0
72 : : */
73 : : char *
74 : 19 : valent_string_to_markup (const char *text)
75 : : {
76 : 38 : g_autofree char *result = NULL;
77 : 19 : int text_len = 0;
78 : 19 : g_autoptr (GError) error = NULL;
79 : :
80 [ + + ]: 19 : if G_UNLIKELY (text == NULL)
81 : : return NULL;
82 : :
83 [ + + ]: 15 : if G_UNLIKELY (uri_regex == NULL)
84 : : {
85 : 6 : email_regex = g_regex_new (EMAIL_PATTERN, URI_FLAGS, 0, NULL);
86 : 6 : uri_regex = g_regex_new (URL_PATTERN"|"EMAIL_PATTERN, URI_FLAGS, 0, NULL);
87 : : }
88 : :
89 : 15 : text_len = strlen (text);
90 : 15 : result = g_regex_replace_eval (uri_regex,
91 : : text,
92 : : text_len,
93 : : 0,
94 : : 0,
95 : : valent_ui_replace_eval_uri,
96 : : NULL,
97 : : &error);
98 : :
99 [ - + ]: 15 : if (result == NULL)
100 : : {
101 : 0 : g_warning ("%s: %s: %s", G_STRFUNC, error->message, text);
102 : 0 : return g_markup_escape_text (text, text_len);
103 : : }
104 : :
105 [ + + + - ]: 19 : if (!pango_parse_markup (result, -1, 0, NULL, NULL, NULL, &error) &&
106 : 4 : !g_error_matches (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT))
107 : : {
108 : 0 : g_warning ("%s: %s: %s", G_STRFUNC, error->message, result);
109 : 0 : return g_markup_escape_text (text, text_len);
110 : : }
111 : :
112 : : return g_steal_pointer (&result);
113 : : }
|