NeoMutt  2024-12-12-14-g7b49f7
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
qstyle.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <stdbool.h>
31#include "mutt/lib.h"
32#include "qstyle.h"
33#include "quoted.h"
34
41static void qstyle_free(struct QuoteStyle **ptr)
42{
43 if (!ptr || !*ptr)
44 return;
45
46 struct QuoteStyle *qc = *ptr;
47 FREE(&qc->prefix);
48
49 FREE(ptr);
50}
51
58void qstyle_free_tree(struct QuoteStyle **quote_list)
59{
60 struct QuoteStyle *next = NULL;
61
62 while (*quote_list)
63 {
64 if ((*quote_list)->down)
65 qstyle_free_tree(&((*quote_list)->down));
66 next = (*quote_list)->next;
67 qstyle_free(quote_list);
68 *quote_list = next;
69 }
70}
71
76static struct QuoteStyle *qstyle_new(void)
77{
78 return MUTT_MEM_CALLOC(1, struct QuoteStyle);
79}
80
88static void qstyle_insert(struct QuoteStyle *quote_list,
89 struct QuoteStyle *new_class, int index, int *q_level)
90{
91 struct QuoteStyle *q_list = quote_list;
92 new_class->quote_n = -1;
93
94 while (q_list)
95 {
96 if (q_list->quote_n >= index)
97 {
98 q_list->quote_n++;
99 q_list->attr_color = quoted_colors_get(q_list->quote_n);
100 }
101 if (q_list->down)
102 {
103 q_list = q_list->down;
104 }
105 else if (q_list->next)
106 {
107 q_list = q_list->next;
108 }
109 else
110 {
111 while (!q_list->next)
112 {
113 q_list = q_list->up;
114 if (!q_list)
115 break;
116 }
117 if (q_list)
118 q_list = q_list->next;
119 }
120 }
121
122 new_class->quote_n = index;
123 new_class->attr_color = quoted_colors_get(index);
124 (*q_level)++;
125}
126
136struct QuoteStyle *qstyle_classify(struct QuoteStyle **quote_list, const char *qptr,
137 size_t length, bool *force_redraw, int *q_level)
138{
139 struct QuoteStyle *q_list = *quote_list;
140 struct QuoteStyle *qc = NULL, *tmp = NULL, *ptr = NULL, *save = NULL;
141 const char *tail_qptr = NULL;
142 size_t offset, tail_lng;
143 int index = -1;
144
145 /* classify quoting prefix */
146 while (q_list)
147 {
148 if (length <= q_list->prefix_len)
149 {
150 /* case 1: check the top level nodes */
151
152 if (mutt_strn_equal(qptr, q_list->prefix, length))
153 {
154 if (length == q_list->prefix_len)
155 return q_list; /* same prefix: return the current class */
156
157 /* found shorter prefix */
158 if (tmp)
159 {
160 /* found another branch for which tmp is a shorter prefix */
161
162 /* save the next sibling for later */
163 save = q_list->next;
164
165 /* unlink q_list from the top level list */
166 if (q_list->next)
167 q_list->next->prev = q_list->prev;
168 if (q_list->prev)
169 q_list->prev->next = q_list->next;
170
171 /* at this point, we have a tmp->down; link q_list to it */
172 ptr = tmp->down;
173 /* sibling order is important here, q_list should be linked last */
174 while (ptr->next)
175 ptr = ptr->next;
176 ptr->next = q_list;
177 q_list->next = NULL;
178 q_list->prev = ptr;
179 q_list->up = tmp;
180
181 index = q_list->quote_n;
182
183 /* next class to test; as above, we shouldn't go down */
184 q_list = save;
185 }
186 else
187 {
188 /* add a node above q_list */
189 tmp = qstyle_new();
190 tmp->prefix = mutt_strn_dup(qptr, length);
191 tmp->prefix_len = length;
192
193 /* replace q_list by tmp in the top level list */
194 if (q_list->next)
195 {
196 tmp->next = q_list->next;
197 q_list->next->prev = tmp;
198 }
199 if (q_list->prev)
200 {
201 tmp->prev = q_list->prev;
202 q_list->prev->next = tmp;
203 }
204
205 /* make q_list a child of tmp */
206 tmp->down = q_list;
207 q_list->up = tmp;
208
209 /* q_list has no siblings for now */
210 q_list->next = NULL;
211 q_list->prev = NULL;
212
213 /* update the root if necessary */
214 if (q_list == *quote_list)
215 *quote_list = tmp;
216
217 index = q_list->quote_n;
218
219 /* tmp should be the return class too */
220 qc = tmp;
221
222 /* next class to test; if tmp is a shorter prefix for another
223 * node, that node can only be in the top level list, so don't
224 * go down after this point */
225 q_list = tmp->next;
226 }
227
228 /* we found a shorter prefix, so certain quotes have changed classes */
229 *force_redraw = true;
230 continue;
231 }
232 else
233 {
234 /* shorter, but not a substring of the current class: try next */
235 q_list = q_list->next;
236 continue;
237 }
238 }
239 else
240 {
241 /* case 2: try subclassing the current top level node */
242
243 /* tmp != NULL means we already found a shorter prefix at case 1 */
244 if (!tmp && mutt_strn_equal(qptr, q_list->prefix, q_list->prefix_len))
245 {
246 /* ok, it's a subclass somewhere on this branch */
247
248 ptr = q_list;
249 offset = q_list->prefix_len;
250
251 q_list = q_list->down;
252 tail_lng = length - offset;
253 tail_qptr = qptr + offset;
254
255 while (q_list)
256 {
257 if (length <= q_list->prefix_len)
258 {
259 if (mutt_strn_equal(tail_qptr, (q_list->prefix) + offset, tail_lng))
260 {
261 /* same prefix: return the current class */
262 if (length == q_list->prefix_len)
263 return q_list;
264
265 /* found shorter common prefix */
266 if (!tmp)
267 {
268 /* add a node above q_list */
269 tmp = qstyle_new();
270 tmp->prefix = mutt_strn_dup(qptr, length);
271 tmp->prefix_len = length;
272
273 /* replace q_list by tmp */
274 if (q_list->next)
275 {
276 tmp->next = q_list->next;
277 q_list->next->prev = tmp;
278 }
279 if (q_list->prev)
280 {
281 tmp->prev = q_list->prev;
282 q_list->prev->next = tmp;
283 }
284
285 /* make q_list a child of tmp */
286 tmp->down = q_list;
287 tmp->up = q_list->up;
288 q_list->up = tmp;
289 if (tmp->up->down == q_list)
290 tmp->up->down = tmp;
291
292 /* q_list has no siblings */
293 q_list->next = NULL;
294 q_list->prev = NULL;
295
296 index = q_list->quote_n;
297
298 /* tmp should be the return class too */
299 qc = tmp;
300
301 /* next class to test */
302 q_list = tmp->next;
303 }
304 else
305 {
306 /* found another branch for which tmp is a shorter prefix */
307
308 /* save the next sibling for later */
309 save = q_list->next;
310
311 /* unlink q_list from the top level list */
312 if (q_list->next)
313 q_list->next->prev = q_list->prev;
314 if (q_list->prev)
315 q_list->prev->next = q_list->next;
316
317 /* at this point, we have a tmp->down; link q_list to it */
318 ptr = tmp->down;
319 while (ptr->next)
320 ptr = ptr->next;
321 ptr->next = q_list;
322 q_list->next = NULL;
323 q_list->prev = ptr;
324 q_list->up = tmp;
325
326 index = q_list->quote_n;
327
328 /* next class to test */
329 q_list = save;
330 }
331
332 /* we found a shorter prefix, so we need a redraw */
333 *force_redraw = true;
334 continue;
335 }
336 else
337 {
338 q_list = q_list->next;
339 continue;
340 }
341 }
342 else
343 {
344 /* longer than the current prefix: try subclassing it */
345 if (!tmp && mutt_strn_equal(tail_qptr, (q_list->prefix) + offset,
346 q_list->prefix_len - offset))
347 {
348 /* still a subclass: go down one level */
349 ptr = q_list;
350 offset = q_list->prefix_len;
351
352 q_list = q_list->down;
353 tail_lng = length - offset;
354 tail_qptr = qptr + offset;
355
356 continue;
357 }
358 else
359 {
360 /* nope, try the next prefix */
361 q_list = q_list->next;
362 continue;
363 }
364 }
365 }
366
367 /* still not found so far: add it as a sibling to the current node */
368 if (!qc)
369 {
370 tmp = qstyle_new();
371 tmp->prefix = mutt_strn_dup(qptr, length);
372 tmp->prefix_len = length;
373
374 if (ptr->down)
375 {
376 tmp->next = ptr->down;
377 ptr->down->prev = tmp;
378 }
379 ptr->down = tmp;
380 tmp->up = ptr;
381
382 tmp->quote_n = (*q_level)++;
383 tmp->attr_color = quoted_colors_get(tmp->quote_n);
384
385 return tmp;
386 }
387 else
388 {
389 if (index != -1)
390 qstyle_insert(*quote_list, tmp, index, q_level);
391
392 return qc;
393 }
394 }
395 else
396 {
397 /* nope, try the next prefix */
398 q_list = q_list->next;
399 continue;
400 }
401 }
402 }
403
404 if (!qc)
405 {
406 /* not found so far: add it as a top level class */
407 qc = qstyle_new();
408 qc->prefix = mutt_strn_dup(qptr, length);
409 qc->prefix_len = length;
410 qc->quote_n = (*q_level)++;
412
413 if (*quote_list)
414 {
415 if ((*quote_list)->next)
416 {
417 qc->next = (*quote_list)->next;
418 qc->next->prev = qc;
419 }
420 (*quote_list)->next = qc;
421 qc->prev = *quote_list;
422 }
423 else
424 {
425 *quote_list = qc;
426 }
427 }
428
429 if (index != -1)
430 qstyle_insert(*quote_list, tmp, index, q_level);
431
432 return qc;
433}
434
441static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
442{
443 if (!quote_list)
444 return;
445
446 if (num_qlevel > 0)
447 {
448 quote_list->attr_color = quoted_colors_get(*cur_qlevel);
449 *cur_qlevel = (*cur_qlevel + 1) % num_qlevel;
450 }
451 else
452 {
453 quote_list->attr_color = NULL;
454 }
455
456 qstyle_recurse(quote_list->down, num_qlevel, cur_qlevel);
457 qstyle_recurse(quote_list->next, num_qlevel, cur_qlevel);
458}
459
464void qstyle_recolor(struct QuoteStyle *quote_list)
465{
466 if (!quote_list)
467 return;
468
469 int num = quoted_colors_num_used();
470 int cur = 0;
471
472 qstyle_recurse(quote_list, num, &cur);
473}
#define FREE(x)
Definition: memory.h:55
#define MUTT_MEM_CALLOC(n, type)
Definition: memory.h:40
Convenience wrapper for the library headers.
char * mutt_strn_dup(const char *begin, size_t len)
Duplicate a sub-string.
Definition: string.c:380
bool mutt_strn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings (to a maximum), safely.
Definition: string.c:425
struct QuoteStyle * qstyle_classify(struct QuoteStyle **quote_list, const char *qptr, size_t length, bool *force_redraw, int *q_level)
Find a style for a string.
Definition: qstyle.c:136
void qstyle_recolor(struct QuoteStyle *quote_list)
Recolour quotes after colour changes.
Definition: qstyle.c:464
static void qstyle_recurse(struct QuoteStyle *quote_list, int num_qlevel, int *cur_qlevel)
Update the quoting styles after colour changes.
Definition: qstyle.c:441
void qstyle_free_tree(struct QuoteStyle **quote_list)
Free an entire tree of QuoteStyle.
Definition: qstyle.c:58
static void qstyle_insert(struct QuoteStyle *quote_list, struct QuoteStyle *new_class, int index, int *q_level)
Insert a new quote colour class into a list.
Definition: qstyle.c:88
static struct QuoteStyle * qstyle_new(void)
Create a new QuoteStyle.
Definition: qstyle.c:76
static void qstyle_free(struct QuoteStyle **ptr)
Free a single QuoteStyle object.
Definition: qstyle.c:41
Quoted style.
struct AttrColor * quoted_colors_get(int q)
Return the color of a quote, cycling through the used quotes.
Definition: quoted.c:86
int quoted_colors_num_used(void)
Return the number of used quotes.
Definition: quoted.c:97
Quoted-Email colours.
Style of quoted text.
Definition: qstyle.h:56
struct AttrColor * attr_color
Colour and attribute of the text.
Definition: qstyle.h:58
struct QuoteStyle * next
Different quoting styles at the same level.
Definition: qstyle.h:61
struct QuoteStyle * up
Definition: qstyle.h:62
size_t prefix_len
Length of the prefix string.
Definition: qstyle.h:60
struct QuoteStyle * prev
Definition: qstyle.h:61
char * prefix
Prefix string, e.g. "> ".
Definition: qstyle.h:59
struct QuoteStyle * down
Parent (less quoted) and child (more quoted) levels.
Definition: qstyle.h:62
int quote_n
The quoteN colour index for this level.
Definition: qstyle.h:57