source: fmtext.c @ 9866c3a

barnowl_perlaimdebianrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 9866c3a was 9866c3a, checked in by Alejandro R. Sedeño <asedeno@mit.edu>, 14 years ago
Reworked the fmtext format to use in-line formatting. Characters used for formatting are part of Unicode Supplemental Private Area-B, or Plane 16. fmtext no longer need 5x the text space to store formatting information, though they are harder to change at arbitrary points. This was something we hardly ever did anyhow, and we can still do everything we need to do. fmtext keeps a pair of colors and a char for default attributes to be applied when necessary. Searching is now done inline at owl_fmtext_waddstr() rather than specifying a section of the string to be reversed. This probably still needs some cleanup and more comments, but it works.
  • Property mode set to 100644
File size: 24.3 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
4
5static const char fileIdent[] = "$Id$";
6
7/* initialize an fmtext with no data */
8void owl_fmtext_init_null(owl_fmtext *f)
9{
10  f->textlen = 0;
11  f->bufflen = 5;
12  f->textbuff = owl_malloc(5);
13  f->textbuff[0] = 0;
14  f->default_attrs = OWL_FMTEXT_ATTR_NONE;
15  f->default_fgcolor = OWL_COLOR_DEFAULT;
16  f->default_fgcolor = OWL_COLOR_DEFAULT;
17}
18
19/* Clear the data from an fmtext, but don't deallocate memory. This
20   fmtext can then be appended to again. */
21void owl_fmtext_clear(owl_fmtext *f)
22{
23  f->textlen = 0;
24  f->textbuff[0] = 0;
25  f->default_attrs = OWL_FMTEXT_ATTR_NONE;
26  f->default_fgcolor = OWL_COLOR_DEFAULT;
27  f->default_fgcolor = OWL_COLOR_DEFAULT;
28}
29
30void _owl_fmtext_realloc(owl_fmtext *f, int newlen) /*noproto*/
31{
32    if(newlen + 1 > f->bufflen) {
33      f->textbuff = owl_realloc(f->textbuff, newlen + 1);
34      f->bufflen = newlen+1;
35  }
36}
37
38int _owl_fmtext_is_format_char(gunichar c) /*noproto*/
39{
40  if ((c & ~OWL_FMTEXT_UC_ATTR_MASK) == OWL_FMTEXT_UC_ATTR) return 1;
41  if ((c & ~(OWL_FMTEXT_UC_ALLCOLOR_MASK)) == OWL_FMTEXT_UC_COLOR_BASE) return 1;
42  return 0;
43}
44/* append text to the end of 'f' with attribute 'attr' and color
45 * 'color'
46 */
47void owl_fmtext_append_attr(owl_fmtext *f, char *text, char attr, short fgcolor, short bgcolor)
48{
49  char attrbuff[6];
50  int newlen, a = 0, fg = 0, bg = 0;
51 
52  if (attr != OWL_FMTEXT_ATTR_NONE) a=1;
53  if (fgcolor != OWL_COLOR_DEFAULT) fg=1;
54  if (bgcolor != OWL_COLOR_DEFAULT) bg=1;
55
56  /* Plane-16 characters in UTF-8 are 4 bytes long. */
57  newlen = strlen(f->textbuff) + strlen(text) + (8 * (a + fg + bg));
58  _owl_fmtext_realloc(f, newlen);
59
60  /* Set attributes */
61  if (a) {
62    memset(attrbuff,0,6);
63    g_unichar_to_utf8(OWL_FMTEXT_UC_ATTR | attr, attrbuff);
64    strcat(f->textbuff, attrbuff);     
65  }
66  if (fg) {
67    memset(attrbuff,0,6);
68    g_unichar_to_utf8(OWL_FMTEXT_UC_FGCOLOR | fgcolor, attrbuff);
69    strcat(f->textbuff, attrbuff);     
70  }
71  if (bg) {
72    memset(attrbuff,0,6);
73    g_unichar_to_utf8(OWL_FMTEXT_UC_BGCOLOR | bgcolor, attrbuff);
74    strcat(f->textbuff, attrbuff);     
75  }
76 
77  strcat(f->textbuff, text);
78
79  /* Reset attributes */
80  if (bg) strcat(f->textbuff, OWL_FMTEXT_UTF8_BGDEFAULT);
81  if (fg) strcat(f->textbuff, OWL_FMTEXT_UTF8_FGDEFAULT);
82  if (a)  strcat(f->textbuff, OWL_FMTEXT_UTF8_ATTR_NONE);
83  f->textlen=newlen;
84}
85
86/* Append normal, uncolored text 'text' to 'f' */
87void owl_fmtext_append_normal(owl_fmtext *f, char *text)
88{
89  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
90}
91
92/* Append normal text 'text' to 'f' with color 'color' */
93void owl_fmtext_append_normal_color(owl_fmtext *f, char *text, int fgcolor, int bgcolor)
94{
95  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_NONE, fgcolor, bgcolor);
96}
97
98/* Append bold text 'text' to 'f' */
99void owl_fmtext_append_bold(owl_fmtext *f, char *text)
100{
101  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
102}
103
104/* Append reverse video text 'text' to 'f' */
105void owl_fmtext_append_reverse(owl_fmtext *f, char *text)
106{
107  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
108}
109
110/* Append reversed and bold, uncolored text 'text' to 'f' */
111void owl_fmtext_append_reversebold(owl_fmtext *f, char *text)
112{
113  owl_fmtext_append_attr(f, text, OWL_FMTEXT_ATTR_REVERSE | OWL_FMTEXT_ATTR_BOLD, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
114}
115
116/* Add the attribute 'attr' to the default atts for the text in 'f' */
117void owl_fmtext_addattr(owl_fmtext *f, char attr)
118{
119  /* add the attribute to all text */
120  f->default_attrs |= attr;
121}
122
123/* Set the default foreground color for this fmtext to 'color'.
124 * Only affects text that is colored default.
125 */
126void owl_fmtext_colorize(owl_fmtext *f, int color)
127{
128  f->default_fgcolor = color;
129}
130
131/* Set the default foreground color for this fmtext to 'color'.
132 * Only affects text that is colored default.
133 */
134void owl_fmtext_colorizebg(owl_fmtext *f, int color)
135{
136  f->default_bgcolor = color;
137}
138
139/* Internal function. Parse attrbute character. */
140void _owl_fmtext_update_attributes(gunichar c, char *attr, short *fgcolor, short *bgcolor) /*noproto*/
141{
142  if ((c & OWL_FMTEXT_UC_ATTR) == OWL_FMTEXT_UC_ATTR) {
143    *attr = c & OWL_FMTEXT_UC_ATTR_MASK;
144  }
145  else if ((c & OWL_FMTEXT_UC_FGCOLOR) == OWL_FMTEXT_UC_FGCOLOR) {
146    *fgcolor = (c == OWL_FMTEXT_UC_FGDEFAULT
147                ? OWL_COLOR_DEFAULT
148                : c & OWL_FMTEXT_UC_COLOR_MASK);
149  }
150  else if ((c & OWL_FMTEXT_UC_BGCOLOR) == OWL_FMTEXT_UC_BGCOLOR) {
151    *bgcolor = (c == OWL_FMTEXT_UC_BGDEFAULT
152                ? OWL_COLOR_DEFAULT
153                : c & OWL_FMTEXT_UC_COLOR_MASK);
154  }
155}
156
157/* Internal function. Scan for attribute characters. */
158void _owl_fmtext_scan_attributes(owl_fmtext *f, int start, char *attr, short *fgcolor, short *bgcolor) /*noproto*/
159{
160  char *p;
161  p = strchr(f->textbuff, OWL_FMTEXT_UC_STARTBYTE_UTF8);
162  while (p && p < f->textbuff + start) {
163    _owl_fmtext_update_attributes(g_utf8_get_char(p), attr, fgcolor, bgcolor);
164    p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
165  }
166} 
167
168/* Internal function.  Append text from 'in' between index 'start' and
169 * 'stop' to the end of 'f'
170 */
171void _owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in, int start, int stop) /*noproto*/
172{
173  char attrbuff[6];
174  int newlen, a = 0, fg = 0, bg = 0;
175  char attr = 0;
176  short fgcolor = OWL_COLOR_DEFAULT;
177  short bgcolor = OWL_COLOR_DEFAULT;
178
179  _owl_fmtext_scan_attributes(in, start, &attr, &fgcolor, &bgcolor);
180  if (attr != OWL_FMTEXT_ATTR_NONE) a=1;
181  if (fgcolor != OWL_COLOR_DEFAULT) fg=1;
182  if (bgcolor != OWL_COLOR_DEFAULT) bg=1;
183
184  /* We will reset to defaults after appending the text. We may need
185     to set initial attributes. */
186  newlen=strlen(f->textbuff)+(stop-start+1) + (4 * (a + fg + bg)) + 12;
187  _owl_fmtext_realloc(f, newlen);
188
189  if (a) {
190    memset(attrbuff,0,6);
191    g_unichar_to_utf8(OWL_FMTEXT_UC_ATTR | attr, attrbuff);
192    strcat(f->textbuff, attrbuff);     
193  }
194  if (fg) {
195    memset(attrbuff,0,6);
196    g_unichar_to_utf8(OWL_FMTEXT_UC_FGCOLOR | fgcolor, attrbuff);
197    strcat(f->textbuff, attrbuff);     
198  }
199  if (bg) {
200    memset(attrbuff,0,6);
201    g_unichar_to_utf8(OWL_FMTEXT_UC_BGCOLOR | bgcolor, attrbuff);
202    strcat(f->textbuff, attrbuff);     
203  }
204
205  strncat(f->textbuff, in->textbuff+start, stop-start+1);
206
207  /* Reset attributes */
208  strcat(f->textbuff, OWL_FMTEXT_UTF8_BGDEFAULT);
209  strcat(f->textbuff, OWL_FMTEXT_UTF8_FGDEFAULT);
210  strcat(f->textbuff, OWL_FMTEXT_UTF8_ATTR_NONE);
211
212  f->textbuff[newlen]='\0';
213  f->textlen=newlen;
214}
215
216/* append fmtext 'in' to 'f' */
217void owl_fmtext_append_fmtext(owl_fmtext *f, owl_fmtext *in)
218{
219  _owl_fmtext_append_fmtext(f, in, 0, in->textlen);
220
221}
222
223/* Append 'nspaces' number of spaces to the end of 'f' */
224void owl_fmtext_append_spaces(owl_fmtext *f, int nspaces)
225{
226  int i;
227  for (i=0; i<nspaces; i++) {
228    owl_fmtext_append_normal(f, " ");
229  }
230}
231
232/* Return a plain version of the fmtext.  Caller is responsible for
233 * freeing the return
234 */
235char *owl_fmtext_print_plain(owl_fmtext *f)
236{
237  char *r, *s, *p;
238  r = owl_malloc(f->bufflen);
239  r[0] = '\0';
240  s = f->textbuff;
241  /* Find next possible format character. */
242  p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
243  while(p) {
244    /* If it's a format character, copy up to it, and skip all
245       immediately following format characters. */
246    if (_owl_fmtext_is_format_char(g_utf8_get_char(p))) {
247      strncat(r, s, p-s);
248      p = g_utf8_next_char(p);
249      while (p && _owl_fmtext_is_format_char(g_utf8_get_char(p))) {
250        p = g_utf8_next_char(p);
251      }
252      s = p;
253      p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
254    }
255    else {
256      p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
257    }
258  }
259  if (s) strcat(r,s);
260  return(r);
261}
262
263void _owl_fmtext_wattrset(WINDOW *w, int attrs) /*noproto*/
264{
265  wattrset(w, A_NORMAL);
266  if (attrs & OWL_FMTEXT_ATTR_BOLD) wattron(w, A_BOLD);
267  if (attrs & OWL_FMTEXT_ATTR_REVERSE) wattron(w, A_REVERSE);
268  if (attrs & OWL_FMTEXT_ATTR_UNDERLINE) wattron(w, A_UNDERLINE);
269}
270/* add the formatted text to the curses window 'w'.  The window 'w'
271 * must already be initiatlized with curses
272 */
273void owl_fmtext_curs_waddstr(owl_fmtext *f, WINDOW *w, int do_search)
274{
275  /* char *tmpbuff; */
276  /* int position, trans1, trans2, trans3, len, lastsame; */
277  char *s, *p;
278  char attr;
279  short fg, bg;
280  int search_results, search_len;
281 
282  if (w==NULL) {
283    owl_function_debugmsg("Hit a null window in owl_fmtext_curs_waddstr.");
284    return;
285  }
286
287  search_results = (do_search
288                    ? owl_fmtext_search(f, owl_global_get_search_string(&g))
289                    : NULL);
290  if (search_results) search_len = strlen(owl_global_get_search_string(&g));
291  s = f->textbuff;
292  /* Set default attributes. */
293  attr = f->default_attrs;
294  fg = f->default_fgcolor;
295  bg = f->default_bgcolor;
296  _owl_fmtext_wattrset(w, attr);
297  if (owl_global_get_hascolors(&g)) {
298    short pair;
299    pair = owl_fmtext_get_colorpair(fg, bg);
300    if (pair != -1) {
301      wcolor_set(w,pair,NULL);
302    }
303  }
304
305  /* Find next possible format character. */
306  p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
307  while(p) {
308    if (_owl_fmtext_is_format_char(g_utf8_get_char(p))) {
309      /* Deal with all text from last insert to here. */
310      char tmp;
311   
312      tmp = p[0];
313      p[0] = '\0';
314      if (search_results) {
315        /* Search is active, so highlight search results. */
316        char tmp2, *ss;
317        ss = stristr(s, owl_global_get_search_string(&g));
318        while (ss) {
319          /* Found search string, highlight it. */
320
321          tmp2 = ss[0];
322          ss[0] = '\0';
323          waddstr(w, s);
324          ss[0] = tmp2;
325
326          _owl_fmtext_wattrset(w,attr ^ A_REVERSE);
327
328          tmp2 = ss[search_len];
329          ss[search_len] = '\0';
330          waddstr(w, ss);
331          ss[search_len] = tmp2;
332
333          _owl_fmtext_wattrset(w,attr);
334
335          s = ss + search_len;
336          ss = stristr(s, owl_global_get_search_string(&g));
337        }
338      }
339      /* Deal with remaining part of string. */
340      waddstr(w, s);
341      p[0] = tmp;
342
343      /* Deal with new attributes. Initialize to defaults, then
344         process all consecutive formatting characters. */
345      attr = f->default_attrs;
346      fg = f->default_fgcolor;
347      bg = f->default_bgcolor;
348      while (p && _owl_fmtext_is_format_char(g_utf8_get_char(p))) {
349        _owl_fmtext_update_attributes(g_utf8_get_char(p), &attr, &fg, &bg);
350        p = g_utf8_next_char(p);
351      }
352      _owl_fmtext_wattrset(w, attr | f->default_attrs);
353      if (owl_global_get_hascolors(&g)) {
354        if (fg == OWL_COLOR_DEFAULT) fg = f->default_fgcolor;
355        if (bg == OWL_COLOR_DEFAULT) bg = f->default_bgcolor;
356        short pair;
357        pair = owl_fmtext_get_colorpair(fg, bg);
358        if (pair != -1) {
359          wcolor_set(w,pair,NULL);
360        }
361      }
362      /* Advance to next non-formatting character. */
363      s = p;
364      p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
365    }
366    else {
367      p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
368    }
369  }
370  if (s) {
371    waddstr(w, s);
372  }
373}
374
375
376/* start with line 'aline' (where the first line is 0) and print
377 * 'lines' number of lines into 'out'
378 */
379int owl_fmtext_truncate_lines(owl_fmtext *in, int aline, int lines, owl_fmtext *out)
380{
381  char *ptr1, *ptr2;
382  int i, offset;
383 
384  /* find the starting line */
385  ptr1 = in->textbuff;
386  for (i = 0; i < aline; i++) {
387    ptr1 = strchr(ptr1, '\n');
388    if (!ptr1) return(-1);
389    ptr1++;
390  }
391 
392  /* ptr1 now holds the starting point */
393
394  /* copy in the next 'lines' lines */
395  if (lines < 1) return(-1);
396
397  for (i = 0; i < lines; i++) {
398    offset = ptr1 - in->textbuff;
399    ptr2 = strchr(ptr1, '\n');
400    if (!ptr2) {
401      _owl_fmtext_append_fmtext(out, in, offset, (in->textlen) - 1);
402      return(-1);
403    }
404    _owl_fmtext_append_fmtext(out, in, offset, (ptr2 - ptr1) + offset);
405    ptr1 = ptr2 + 1;
406  }
407  return(0);
408}
409
410/* Truncate the message so that each line begins at column 'acol' and
411 * ends at 'bcol' or sooner.  The first column is number 0.  The new
412 * message is placed in 'out'.  The message is * expected to end in a
413 * new line for now
414 */
415void owl_fmtext_truncate_cols(owl_fmtext *in, int acol, int bcol, owl_fmtext *out)
416{
417  char *ptr_s, *ptr_e, *ptr_c, *last;
418  int col, st, padding, chwidth;
419
420  last=in->textbuff+in->textlen-1;
421  ptr_s=in->textbuff;
422  while (ptr_s <= last) {
423    ptr_e=strchr(ptr_s, '\n');
424    if (!ptr_e) {
425      /* but this shouldn't happen if we end in a \n */
426      break;
427    }
428   
429    if (ptr_e == ptr_s) {
430      owl_fmtext_append_normal(out, "\n");
431      ++ptr_s;
432      continue;
433    }
434
435    col = 0;
436    st = 0;
437    padding = 0;
438    ptr_c = ptr_s;
439    while(col <= bcol && ptr_c < ptr_e) {
440      gunichar c = g_utf8_get_char(ptr_c);
441      if (!_owl_fmtext_is_format_char(c)) {
442        chwidth = wcwidth(c);
443     
444        if (col + chwidth > bcol)
445          break;
446        if (col >= acol) {
447          if (st == 0) {
448            ptr_s = ptr_c;
449            padding = col - acol;
450            ++st;
451          }
452        }
453        col += chwidth;
454        chwidth = 0;
455      }
456      ptr_c = g_utf8_next_char(ptr_c);
457    }
458    if (st) {
459      /* lead padding */
460      owl_fmtext_append_spaces(out, padding);
461      if (ptr_c[0] & 0x80) {
462        if (col + chwidth == bcol)
463          ptr_c = g_utf8_next_char(ptr_c);
464        _owl_fmtext_append_fmtext(out, in, ptr_s - in->textbuff, ptr_c - 1 - in->textbuff);     
465        owl_fmtext_append_normal(out, "\n");
466      }
467      else {
468        _owl_fmtext_append_fmtext(out, in, ptr_s - in->textbuff, ptr_c - in->textbuff);
469      }
470    }
471    else {
472      owl_fmtext_append_normal(out, "\n");
473    }
474    ptr_s = g_utf8_next_char(ptr_e);
475  }
476}
477
478/* Return the number of lines in 'f' */
479int owl_fmtext_num_lines(owl_fmtext *f)
480{
481  int lines, i;
482
483  if (f->textlen==0) return(0);
484 
485  lines=0;
486  for (i=0; i<f->textlen; i++) {
487    if (f->textbuff[i]=='\n') lines++;
488  }
489
490  /* if the last char wasn't a \n there's one more line */
491  if (f->textbuff[i-1]!='\n') lines++;
492
493  return(lines);
494}
495
496char *owl_fmtext_get_text(owl_fmtext *f)
497{
498  return(f->textbuff);
499}
500
501/* set the charater at 'index' to be 'char'.  If index is out of
502 * bounds don't do anything. If c or char at index is not ASCII, don't
503 * do anything because it's not UTF-8 safe. */
504void owl_fmtext_set_char(owl_fmtext *f, int index, char ch)
505{
506  if ((index < 0) || (index > f->textlen-1)) return;
507  /* NOT ASCII*/
508  if (f->textbuff[index] & 0x80 || ch & 0x80) return; 
509  f->textbuff[index]=ch;
510}
511
512/* Make a copy of the fmtext 'src' into 'dst' */
513void owl_fmtext_copy(owl_fmtext *dst, owl_fmtext *src)
514{
515  int mallocsize;
516
517  if (src->textlen==0) {
518    mallocsize=5;
519  } else {
520    mallocsize=src->textlen+2;
521  }
522  dst->textlen=src->textlen;
523  dst->textbuff=owl_malloc(mallocsize);
524  memcpy(dst->textbuff, src->textbuff, src->textlen+1);
525  dst->default_attrs = src->default_attrs;
526  dst->default_fgcolor = src->default_fgcolor;
527  dst->default_bgcolor = src->default_bgcolor;
528}
529
530/* return 1 if the string is found, 0 if not.  This is a case
531 *  insensitive search.
532 */
533int owl_fmtext_search(owl_fmtext *f, char *string)
534{
535  if (stristr(f->textbuff, string)) return(1);
536  return(0);
537}
538
539
540/* Append the text 'text' to 'f' and interpret the zephyr style
541 * formatting syntax to set appropriate attributes.
542 */
543void owl_fmtext_append_ztext(owl_fmtext *f, char *text)
544{
545  int stacksize, curattrs, curcolor;
546  char *ptr, *txtptr, *buff, *tmpptr;
547  int attrstack[32], chrstack[32], colorstack[32];
548
549  curattrs=OWL_FMTEXT_ATTR_NONE;
550  curcolor=OWL_COLOR_DEFAULT;
551  stacksize=0;
552  txtptr=text;
553  while (1) {
554    ptr=strpbrk(txtptr, "@{[<()>]}");
555    if (!ptr) {
556      /* add all the rest of the text and exit */
557      owl_fmtext_append_attr(f, txtptr, curattrs, curcolor, OWL_COLOR_DEFAULT);
558      return;
559    } else if (ptr[0]=='@') {
560      /* add the text up to this point then deal with the stack */
561      buff=owl_malloc(ptr-txtptr+20);
562      strncpy(buff, txtptr, ptr-txtptr);
563      buff[ptr-txtptr]='\0';
564      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
565      owl_free(buff);
566
567      /* update pointer to point at the @ */
568      txtptr=ptr;
569
570      /* now the stack */
571
572      /* if we've hit our max stack depth, print the @ and move on */
573      if (stacksize==32) {
574        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
575        txtptr++;
576        continue;
577      }
578
579      /* if it's an @@, print an @ and continue */
580      if (txtptr[1]=='@') {
581        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
582        txtptr+=2;
583        continue;
584      }
585       
586      /* if there's no opener, print the @ and continue */
587      tmpptr=strpbrk(txtptr, "(<[{ ");
588      if (!tmpptr || tmpptr[0]==' ') {
589        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
590        txtptr++;
591        continue;
592      }
593
594      /* check what command we've got, push it on the stack, start
595         using it, and continue ... unless it's a color command */
596      buff=owl_malloc(tmpptr-ptr+20);
597      strncpy(buff, ptr, tmpptr-ptr);
598      buff[tmpptr-ptr]='\0';
599      if (!strcasecmp(buff, "@bold")) {
600        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
601        chrstack[stacksize]=tmpptr[0];
602        colorstack[stacksize]=curcolor;
603        stacksize++;
604        curattrs|=OWL_FMTEXT_ATTR_BOLD;
605        txtptr+=6;
606        owl_free(buff);
607        continue;
608      } else if (!strcasecmp(buff, "@b")) {
609        attrstack[stacksize]=OWL_FMTEXT_ATTR_BOLD;
610        chrstack[stacksize]=tmpptr[0];
611        colorstack[stacksize]=curcolor;
612        stacksize++;
613        curattrs|=OWL_FMTEXT_ATTR_BOLD;
614        txtptr+=3;
615        owl_free(buff);
616        continue;
617      } else if (!strcasecmp(buff, "@i")) {
618        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
619        chrstack[stacksize]=tmpptr[0];
620        colorstack[stacksize]=curcolor;
621        stacksize++;
622        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
623        txtptr+=3;
624        owl_free(buff);
625        continue;
626      } else if (!strcasecmp(buff, "@italic")) {
627        attrstack[stacksize]=OWL_FMTEXT_ATTR_UNDERLINE;
628        chrstack[stacksize]=tmpptr[0];
629        colorstack[stacksize]=curcolor;
630        stacksize++;
631        curattrs|=OWL_FMTEXT_ATTR_UNDERLINE;
632        txtptr+=8;
633        owl_free(buff);
634        continue;
635      } else if (!strcasecmp(buff, "@")) {
636        attrstack[stacksize]=OWL_FMTEXT_ATTR_NONE;
637        chrstack[stacksize]=tmpptr[0];
638        colorstack[stacksize]=curcolor;
639        stacksize++;
640        txtptr+=2;
641        owl_free(buff);
642        continue;
643
644        /* if it's a color read the color, set the current color and
645           continue */
646      } else if (!strcasecmp(buff, "@color") 
647                 && owl_global_get_hascolors(&g)
648                 && owl_global_is_colorztext(&g)) {
649        owl_free(buff);
650        txtptr+=7;
651        tmpptr=strpbrk(txtptr, "@{[<()>]}");
652        if (tmpptr &&
653            ((txtptr[-1]=='(' && tmpptr[0]==')') ||
654             (txtptr[-1]=='<' && tmpptr[0]=='>') ||
655             (txtptr[-1]=='[' && tmpptr[0]==']') ||
656             (txtptr[-1]=='{' && tmpptr[0]=='}'))) {
657
658          /* grab the color name */
659          buff=owl_malloc(tmpptr-txtptr+20);
660          strncpy(buff, txtptr, tmpptr-txtptr);
661          buff[tmpptr-txtptr]='\0';
662
663          /* set it as the current color */
664          curcolor=owl_util_string_to_color(buff);
665          if (curcolor==-1) curcolor=OWL_COLOR_DEFAULT;
666          owl_free(buff);
667          txtptr=tmpptr+1;
668          continue;
669
670        } else {
671
672        }
673
674      } else {
675        /* if we didn't understand it, we'll print it.  This is different from zwgc
676         * but zwgc seems to be smarter about some screw cases than I am
677         */
678        owl_fmtext_append_attr(f, "@", curattrs, curcolor, OWL_COLOR_DEFAULT);
679        txtptr++;
680        continue;
681      }
682
683    } else if (ptr[0]=='}' || ptr[0]==']' || ptr[0]==')' || ptr[0]=='>') {
684      /* add the text up to this point first */
685      buff=owl_malloc(ptr-txtptr+20);
686      strncpy(buff, txtptr, ptr-txtptr);
687      buff[ptr-txtptr]='\0';
688      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
689      owl_free(buff);
690
691      /* now deal with the closer */
692      txtptr=ptr;
693
694      /* first, if the stack is empty we must bail (just print and go) */
695      if (stacksize==0) {
696        buff=owl_malloc(5);
697        buff[0]=ptr[0];
698        buff[1]='\0';
699        owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
700        owl_free(buff);
701        txtptr++;
702        continue;
703      }
704
705      /* if the closing char is what's on the stack, turn off the
706         attribue and pop the stack */
707      if ((ptr[0]==')' && chrstack[stacksize-1]=='(') ||
708          (ptr[0]=='>' && chrstack[stacksize-1]=='<') ||
709          (ptr[0]==']' && chrstack[stacksize-1]=='[') ||
710          (ptr[0]=='}' && chrstack[stacksize-1]=='{')) {
711        int i;
712        stacksize--;
713        curattrs=OWL_FMTEXT_ATTR_NONE;
714        curcolor = colorstack[stacksize];
715        for (i=0; i<stacksize; i++) {
716          curattrs|=attrstack[i];
717        }
718        txtptr+=1;
719        continue;
720      } else {
721        /* otherwise print and continue */
722        buff=owl_malloc(5);
723        buff[0]=ptr[0];
724        buff[1]='\0';
725        owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
726        owl_free(buff);
727        txtptr++;
728        continue;
729      }
730    } else {
731      /* we've found an unattached opener, print everything and move on */
732      buff=owl_malloc(ptr-txtptr+20);
733      strncpy(buff, txtptr, ptr-txtptr+1);
734      buff[ptr-txtptr+1]='\0';
735      owl_fmtext_append_attr(f, buff, curattrs, curcolor, OWL_COLOR_DEFAULT);
736      owl_free(buff);
737      txtptr=ptr+1;
738      continue;
739    }
740  }
741}
742
743/* requires that the list values are strings or NULL.
744 * joins the elements together with join_with.
745 * If format_fn is specified, passes it the list element value
746 * and it will return a string which this needs to free. */
747void owl_fmtext_append_list(owl_fmtext *f, owl_list *l, char *join_with, char *(format_fn)(void*))
748{
749  int i, size;
750  void *elem;
751  char *text;
752
753  size = owl_list_get_size(l);
754  for (i=0; i<size; i++) {
755    elem = (char*)owl_list_get_element(l,i);
756    if (elem && format_fn) {
757      text = format_fn(elem);
758      if (text) {
759        owl_fmtext_append_normal(f, text);
760        owl_free(text);
761      }
762    } else if (elem) {
763      owl_fmtext_append_normal(f, elem);
764    }
765    if ((i < size-1) && join_with) {
766      owl_fmtext_append_normal(f, join_with);
767    }
768  }
769}
770
771/* Free all memory allocated by the object */
772void owl_fmtext_free(owl_fmtext *f)
773{
774  if (f->textbuff) owl_free(f->textbuff);
775}
776
777/*** Color Pair manager ***/
778void owl_fmtext_init_colorpair_mgr(owl_colorpair_mgr *cpmgr)
779{
780  // This could be a bitarray if we wanted to save memory.
781  short i, j;
782  cpmgr->next = 8;
783 
784  // The test is <= because we allocate COLORS+1 entries.
785  cpmgr->pairs = owl_malloc((COLORS+1) * sizeof(short*));
786  for(i = 0; i <= COLORS; i++) {
787    cpmgr->pairs[i] = owl_malloc((COLORS+1) * sizeof(short));
788    for(j = 0; j <= COLORS; j++) {
789      cpmgr->pairs[i][j] = -1;
790    }
791  }
792  if (owl_global_get_hascolors(&g)) {
793    for(i = 0; i < 8; i++) {
794      short fg, bg;
795      if (i >= COLORS) continue;
796      pair_content(i, &fg, &bg);
797      cpmgr->pairs[fg+1][bg+1] = i;
798    }
799  }
800}
801
802/* Reset used list */
803void owl_fmtext_reset_colorpairs()
804{
805  if (owl_global_get_hascolors(&g)) {
806    short i, j;
807    owl_colorpair_mgr *cpmgr = owl_global_get_colorpair_mgr(&g);
808    cpmgr->next = 8;
809   
810    // The test is <= because we allocated COLORS+1 entries.
811    for(i = 0; i <= COLORS; i++) {
812      for(j = 0; j <= COLORS; j++) {
813        cpmgr->pairs[i][j] = -1;
814      }
815    }
816    for(i = 0; i < 8; i++) {
817      short fg, bg;
818      if (i >= COLORS) continue;
819      pair_content(i, &fg, &bg);
820      cpmgr->pairs[fg+1][bg+1] = i;
821    }
822  }
823}
824
825/* Assign pairs by request */
826short owl_fmtext_get_colorpair(int fg, int bg)
827{
828  owl_colorpair_mgr *cpmgr;
829  short pair, default_bg;
830
831#ifdef HAVE_USE_DEFAULT_COLORS
832  if (fg == OWL_COLOR_DEFAULT) fg = -1;
833  default_bg = OWL_COLOR_DEFAULT;
834#else
835  if (fg == OWL_COLOR_DEFAULT) fg = 0;
836  if (bg == OWL_COLOR_DEFAULT) bg = 0;
837  default_bg = COLOR_BLACK;
838#endif
839
840  // looking for a pair we already set up for this draw.
841  cpmgr = owl_global_get_colorpair_mgr(&g);
842  pair = cpmgr->pairs[fg+1][bg+1];
843  if (!(pair != -1 && pair < cpmgr->next)) {
844/*    owl_global_set_needrefresh(&g);*/
845    // If we didn't find a pair, search for a free one to assign.
846    pair = (cpmgr->next < COLOR_PAIRS) ? cpmgr->next : -1;
847    if (pair != -1) {
848      // We found a free pair, initialize it.
849      init_pair(pair, fg, bg);
850      cpmgr->pairs[fg+1][bg+1] = pair;
851      cpmgr->next++;
852    }
853    else if (bg != OWL_COLOR_DEFAULT) {
854      // We still don't have a pair, drop the background color. Too bad.
855      owl_function_debugmsg("colorpairs: color shortage - dropping background color.");
856      pair = owl_fmtext_get_colorpair(fg, OWL_COLOR_DEFAULT);
857    }
858    else {
859      // We still don't have a pair, defaults all around.
860      owl_function_debugmsg("colorpairs: color shortage - dropping foreground and background color.");
861      pair = 0;
862    }
863  }
864  return pair;
865}
Note: See TracBrowser for help on using the repository browser.