1 /* vifm
2  * Copyright (C) 2020 xaizek.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "colored_line.h"
20 
21 #include <stddef.h> /* size_t */
22 #include <stdlib.h> /* free() */
23 #include <string.h> /* memmove() strdup() */
24 
25 #include "../cfg/config.h"
26 #include "../utils/str.h"
27 #include "../utils/utf8.h"
28 #include "color_manager.h"
29 #include "color_scheme.h"
30 #include "ui.h"
31 
32 cline_t
cline_make(void)33 cline_make(void)
34 {
35 	cline_t result = { .line = strdup(""), .attrs = strdup("") };
36 	return result;
37 }
38 
39 int
cline_sync(cline_t * cline,int extra_width)40 cline_sync(cline_t *cline, int extra_width)
41 {
42 	const size_t nchars = utf8_strsw(cline->line) + extra_width;
43 	if(cline->attrs_len < nchars)
44 	{
45 		int width = nchars - cline->attrs_len;
46 		char *const new_attrs = format_str("%s%*s", cline->attrs, width, "");
47 		free(cline->attrs);
48 		cline->attrs = new_attrs;
49 		cline->attrs_len = nchars;
50 	}
51 	return (cline->attrs_len > nchars);
52 }
53 
54 void
cline_set_attr(cline_t * cline,char attr)55 cline_set_attr(cline_t *cline, char attr)
56 {
57 	(void)cline_sync(cline, 1);
58 	cline->attrs[cline->attrs_len - 1] = attr;
59 }
60 
61 void
cline_clear(cline_t * cline)62 cline_clear(cline_t *cline)
63 {
64 	cline->line[0] = '\0';
65 	cline->line_len = 0;
66 	cline->attrs[0] = '\0';
67 	cline->attrs_len = 0;
68 }
69 
70 void
cline_finish(cline_t * cline)71 cline_finish(cline_t *cline)
72 {
73 	if(cline_sync(cline, 0))
74 	{
75 		cline->attrs[--cline->attrs_len] = '\0';
76 	}
77 }
78 
79 void
cline_splice_attrs(cline_t * cline,cline_t * admixture)80 cline_splice_attrs(cline_t *cline, cline_t *admixture)
81 {
82 	char *attrs = admixture->attrs;
83 	if(cline_sync(cline, 0) && admixture->attrs_len > 0U)
84 	{
85 		if(*attrs != ' ')
86 		{
87 			cline->attrs[cline->attrs_len - 1U] = *attrs;
88 		}
89 		++attrs;
90 	}
91 	strappend(&cline->attrs, &cline->attrs_len, attrs);
92 	free(admixture->attrs);
93 }
94 
95 void
cline_print(const cline_t * cline,WINDOW * win,const col_attr_t * def_col)96 cline_print(const cline_t *cline, WINDOW *win, const col_attr_t *def_col)
97 {
98 	cchar_t def_attr;
99 	setcchar(&def_attr, L" ", def_col->attr,
100 			colmgr_get_pair(def_col->fg, def_col->bg), NULL);
101 
102 	const char *line = cline->line;
103 	const char *attrs = cline->attrs;
104 
105 	cchar_t attr = def_attr;
106 	while(*line != '\0')
107 	{
108 		if(*attrs == '0')
109 		{
110 			attr = def_attr;
111 		}
112 		else if(*attrs != ' ')
113 		{
114 			const int color = (USER1_COLOR + (*attrs - '1'));
115 			col_attr_t col = *def_col;
116 			cs_mix_colors(&col, &cfg.cs.color[color]);
117 			setcchar(&attr, L" ", col.attr, colmgr_get_pair(col.fg, col.bg), NULL);
118 		}
119 
120 		const size_t len = utf8_chrw(line);
121 		char char_buf[len + 1];
122 		copy_str(char_buf, sizeof(char_buf), line);
123 		wprinta(win, char_buf, &attr, 0);
124 
125 		line += len;
126 		attrs += utf8_chrsw(char_buf);
127 	}
128 }
129 
130 void
cline_left_ellipsis(cline_t * cline,size_t max_width,const char ell[])131 cline_left_ellipsis(cline_t *cline, size_t max_width, const char ell[])
132 {
133 	if(max_width == 0U)
134 	{
135 		/* No room to print anything. */
136 		cline_clear(cline);
137 		return;
138 	}
139 
140 	size_t width = utf8_strsw(cline->line);
141 	if(width <= max_width)
142 	{
143 		/* No need to change the string. */
144 		return;
145 	}
146 
147 	size_t ell_width = utf8_strsw(ell);
148 	if(max_width <= ell_width)
149 	{
150 		/* Insert as many characters of ellipsis as we can. */
151 		const int prefix = (int)utf8_nstrsnlen(ell, max_width);
152 		cline->line = format_str("%.*s", prefix, ell);
153 		cline->line_len = prefix;
154 		cline->attrs[0] = '\0';
155 		cline->attrs_len = 0;
156 		cline_finish(cline);
157 		return;
158 	}
159 
160 	char *line = cline->line;
161 	char *attrs = cline->attrs;
162 
163 	while(width > max_width - ell_width)
164 	{
165 		int cw = utf8_chrsw(line);
166 		width -= cw;
167 		line += utf8_chrw(line);
168 		attrs += cw;
169 	}
170 
171 	cline->line_len = cline->line_len - (line - cline->line);
172 	memmove(cline->line, line, cline->line_len + 1);
173 	cline->attrs_len = cline->attrs_len - (attrs - cline->attrs);
174 	memmove(cline->attrs, attrs, cline->attrs_len + 1);
175 
176 	strprepend(&cline->line, &cline->line_len, ell);
177 	char spaces[ell_width + 1];
178 	memset(spaces, ' ', sizeof(spaces) - 1);
179 	spaces[ell_width] = '\0';
180 	strprepend(&cline->attrs, &cline->attrs_len, spaces);
181 }
182 
183 void
cline_dispose(cline_t * cline)184 cline_dispose(cline_t *cline)
185 {
186 	update_string(&cline->line, NULL);
187 	cline->line_len = 0;
188 	update_string(&cline->attrs, NULL);
189 	cline->attrs_len = 0;
190 }
191 
192 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
193 /* vim: set cinoptions+=t0 : */
194