revert between 56095 -> 55830 in arch
[AROS.git] / workbench / libs / muimaster / textengine.c
blobb598ebf6ed6722ea0ae8d5375d8173e85677846e
1 /*
2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002-2018, The AROS Development Team.
4 All rights reserved.
6 $Id$
7 */
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <stdio.h>
14 #include <exec/types.h>
15 #include <exec/memory.h>
17 #include <clib/alib_protos.h>
18 #include <intuition/screens.h>
19 #include <proto/exec.h>
20 #include <proto/dos.h>
21 #include <proto/graphics.h>
22 #include <proto/utility.h>
23 #include <proto/intuition.h>
24 #include <proto/muimaster.h>
25 #include <proto/cybergraphics.h>
27 #include "mui.h"
28 #include "textengine.h"
29 #include "imspec.h"
30 #include "support.h"
31 #include "listimage.h"
33 #include "muimaster_intern.h"
35 //#define MYDEBUG 1
36 #include "debug.h"
38 extern struct Library *MUIMasterBase;
40 /* A bit of explanation:
41 * The most important thing, after the datastructure, is the bounds
42 * calculation (especially chunk width).
43 * It will determine where to draw. Drawing is then straightforward.
44 * From the beginning:
45 * the input string is parsed line by line. Each line is formed of chunks
46 * of symbols having the same rendering style. As a special case,
47 * HiChar is a single letter chunk with underline style.
48 * Size calculation is done by calculating for each line the width and
49 * height of each chunk, then line width is the sum of widths and line height
50 * is the max height. *Text width will be max line width, and text height
51 * will be the sum of line heights.* Remember group layout.
52 * Chunk width is affected by soft style of text, and maybe other params.
53 * Drawing is done line by line, chunk by chunk, incrementing each time
54 * the offsets with chunk width or line height.
56 * Italic is looking ugly, no ?
59 #define ZTL_LEFT 1
60 #define ZTL_CENTER 2
61 #define ZTL_RIGHT 3
63 struct zune_context
65 LONG dripen;
66 LONG pen;
67 ULONG style;
68 UBYTE align;
70 ZTextLine *line;
71 CONST_STRPTR text_start;
72 CONST_STRPTR text;
73 CONST_STRPTR imspec;
74 Object *obj; /* Area subclass, see List_CreateImage */
75 struct MUI_AlphaData *alpha_data;
78 ZText *zune_text_new(CONST_STRPTR preparse, CONST_STRPTR content,
79 int argtype, TEXT argbyte);
80 char *zune_text_iso_string(ZText * text);
81 void zune_text_destroy(ZText * text);
82 void zune_text_chunk_new(struct zune_context *zc);
83 static int strlenlf(const char *str);
84 static CONST_STRPTR parse_escape_code(ZTextLine * ztl,
85 struct zune_context *zc, CONST_STRPTR s);
86 static ZTextLine *zune_text_parse_line(CONST_STRPTR * s,
87 struct zune_context *zc, int *argtype, int arg);
88 void zune_text_get_bounds(ZText * text, Object * obj);
89 void zune_text_draw(ZText * text, Object * obj, WORD left, WORD right,
90 WORD top);
91 void zune_text_draw_cursor(ZText * text, Object * obj, WORD left,
92 WORD right, WORD top, LONG cursorx, LONG cursory);
93 void zune_text_draw_single(ZText * text, Object * obj, WORD left,
94 WORD right, WORD top, LONG xpos, LONG ypos, BOOL cursor);
95 int zune_text_get_char_pos(ZText * text, Object * obj, LONG x, LONG y,
96 struct ZTextLine **line_ptr, struct ZTextChunk **chunk_ptr,
97 int *offset_ptr, int *len_ptr);
98 int zune_get_xpos_of_line(ZText * text, Object * obj, LONG y, LONG xpixel);
99 int zune_text_get_lines(ZText * text);
100 int zune_make_cursor_visible(ZText * text, Object * obj, LONG cursorx,
101 LONG cursory, LONG left, LONG top, LONG right, LONG bottom);
102 int zune_text_merge(ZText * text, Object * obj, int x, int y,
103 ZText * tomerge);
106 /**************************************************************************
108 **************************************************************************/
109 ZText *zune_text_new(CONST_STRPTR preparse, CONST_STRPTR content,
110 int argtype, TEXT argbyte)
112 ZText *text;
113 /* STRPTR *lines; */
114 /* int i; */
115 STRPTR dup_content;
116 CONST_STRPTR buf;
117 int preparse_len;
118 struct zune_context zc;
119 int arg;
121 if (!(text = mui_alloc_struct(ZText)))
122 return NULL;
123 NewList((struct List *)&text->lines);
125 if (!content)
126 content = "";
127 preparse_len = preparse ? strlen(preparse) : 0;
128 if (!(dup_content = mui_alloc(preparse_len + strlen(content) + 1)))
130 mui_free(text);
131 return NULL;
134 if (preparse_len)
135 strcpy(dup_content, preparse);
136 strcpy(&dup_content[preparse_len], content);
138 buf = dup_content;
140 zc.pen = -1;
141 zc.dripen = -1;
142 zc.style = FS_NORMAL;
143 zc.align = ZTL_LEFT;
144 zc.imspec = NULL;
145 zc.obj = NULL;
146 zc.alpha_data = NULL;
148 if (argtype == ZTEXT_ARG_HICHAR)
150 /* store lower and upper case in arg */
151 arg = ToLower(argbyte) | (ToUpper(argbyte) << 8);
153 else
154 arg = argbyte;
156 /* the other elements are done by other functions */
158 while (1)
160 struct ZTextLine *ztl =
161 zune_text_parse_line(&buf, &zc, &argtype, arg);
162 if (ztl)
163 AddTail((struct List *)&text->lines, (struct Node *)ztl);
164 else
165 break;
166 if (*buf == '\n')
168 buf++;
169 continue;
171 if (!(*buf))
172 break;
175 mui_free(dup_content);
176 return text;
179 /**************************************************************************
180 Completely frees a ZText
181 **************************************************************************/
182 void zune_text_destroy(ZText * text)
184 struct ZTextLine *ztl;
185 struct ZTextChunk *ztc;
187 while ((ztl = (struct ZTextLine *)RemTail((struct List *)&text->lines)))
189 while ((ztc =
190 (struct ZTextChunk *)RemTail((struct List *)&ztl->
191 chunklist)))
193 if (ztc->str)
194 mui_free(ztc->str);
195 if (ztc->spec)
197 FreeVec((APTR) ztc->spec);
198 if (ztc->image)
200 zune_imspec_cleanup(ztc->image);
203 mui_free(ztc);
205 mui_free(ztl);
207 mui_free(text);
210 /************************************************************/
211 /* Parsing */
212 /************************************************************/
215 /**************************************************************************
216 Allocate and initialize a new text chunk and add it to the list.
217 The context contains the values for the chunk to be created.
218 **************************************************************************/
219 void zune_text_chunk_new(struct zune_context *zc)
221 ZTextChunk *ztc;
223 /* No char has been processed so we needn't allocate anything */
224 if (zc->text == zc->text_start)
225 return;
227 if (!(ztc = mui_alloc_struct(ZTextChunk)))
228 return;
230 ztc->style = zc->style;
231 ztc->dripen = zc->dripen;
232 ztc->pen = zc->pen;
233 if (zc->imspec)
235 D(bug("zune_text_chunk_new: imspec %s\n", zc->imspec));
236 ztc->spec = zc->imspec;
237 zc->imspec = NULL;
239 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
241 else if (zc->obj)
243 ztc->obj = zc->obj;
244 zc->obj = NULL;
246 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
248 else if (zc->alpha_data)
250 ztc->alpha_data = zc->alpha_data;
251 zc->alpha_data = NULL;
253 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
255 else if ((ztc->str = (char *)mui_alloc(zc->text - zc->text_start + 1)))
257 strncpy(ztc->str, zc->text_start, zc->text - zc->text_start + 1);
258 ztc->str[zc->text - zc->text_start] = 0;
259 D(bug("zune_text_chunk_new: string %s\n", ztc->str));
261 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
263 else
265 mui_free(ztc);
270 /**************************************************************************
271 Calculates the length of the string excluding line feed or 0 byte
272 **************************************************************************/
273 static int strlenlf(const char *str)
275 char c;
276 int len = 0;
277 while ((c = *str))
279 if (c == '\n')
280 break;
281 len++;
282 str++;
284 return len;
287 /**************************************************************************
288 Note: Only alignments at the beginning of a line should affect this line
289 (tested in MUI)
290 **************************************************************************/
291 static CONST_STRPTR parse_escape_code(ZTextLine * ztl,
292 struct zune_context *zc, CONST_STRPTR s)
294 unsigned char c;
295 c = *s;
297 zune_text_chunk_new(zc);
298 s++; /* s now points after the command */
299 zc->text_start = zc->text = s;
301 switch (c)
303 case 'c':
304 zc->align = ztl->align = ZTL_CENTER;
305 break;
306 case 'r':
307 zc->align = ztl->align = ZTL_RIGHT;
308 break;
309 case 'l':
310 zc->align = ztl->align = ZTL_LEFT;
311 break;
312 case 'n':
313 zc->style = FS_NORMAL;
314 break;
315 case 'u':
316 zc->style |= FSF_UNDERLINED;
317 break;
318 case 'b':
319 zc->style |= FSF_BOLD;
320 break;
321 case 'i':
322 zc->style |= FSF_ITALIC;
323 break;
324 case 'I': /* image spec */
326 char *t;
328 if (*s != '[')
329 break;
330 s++;
331 /* s points on the first char of imagespec.
332 * Extract it to the trailing ']'.
334 t = strchr(s, ']');
335 if (t == NULL)
336 break;
337 *t = 0;
338 D(bug("imspec = %s\n", s));
339 zc->imspec = StrDup(s);
340 *t = ']';
341 zc->text = t;
342 zune_text_chunk_new(zc);
343 zc->text_start = t + 1;
344 break;
346 case 'O': /* pointer from List_CreateImage */
348 struct ListImage *li;
349 IPTR tmp;
350 char *t;
352 if (*s != '[')
353 break;
354 s++;
355 /* s points on the first char of pointer printed as %08lx.
356 * Extract it to the trailing ']'.
358 t = strchr(s, ']');
359 if (t == NULL)
360 break;
361 *t = 0;
362 if (HexToIPTR(s, &tmp) != -1)
364 D(bug("listimage = %lx\n", tmp));
365 if (tmp == 0)
367 /* According to the MUI autodocs, the result of
368 * CreateImage may be NULL, but then \33O[] has to
369 * simply draw nothing, so it shouldn't be considered
370 * an error.
371 * Without this, AROS crashed, if 00000000 was used.
373 *t = ']';
374 zc->text = t + 1;
375 zc->text_start = t + 1;
376 break;
378 li = (struct ListImage *)tmp;
379 zc->obj = li->obj;
381 *t = ']';
382 zc->text = t;
383 zune_text_chunk_new(zc);
384 zc->text_start = t + 1;
385 break;
387 case 'A': /* pointer to MUI_AlphaData */
389 IPTR tmp;
390 char *t;
392 if (*s != '[')
393 break;
394 s++;
395 /* s points on the first char of pointer printed as %08lx.
396 * Extract it to the trailing ']'.
398 t = strchr(s, ']');
399 if (t == NULL)
400 break;
401 *t = 0;
402 if (HexToIPTR(s, &tmp) != -1)
404 D(bug("image = %lx\n", tmp));
405 if (tmp == 0)
407 /* Draw nothing if image pointer is NULL, as MUI 4 does
408 * (although undocumented)
410 *t = ']';
411 zc->text = t + 1;
412 zc->text_start = t + 1;
413 break;
415 zc->alpha_data = (struct MUI_AlphaData *)tmp;
417 *t = ']';
418 zc->text = t;
419 zune_text_chunk_new(zc);
420 zc->text_start = t + 1;
421 break;
423 case 'P': /* pen number */
425 LONG pen;
426 STRPTR t;
427 UWORD len;
429 if (*s != '[')
430 break;
431 s++;
432 /* s points on the first char of pen from ObtainPen printed as %ld.
433 * Extract it to the trailing ']'.
435 t = strchr(s, ']');
436 if (t == NULL)
437 break;
438 len = t - s;
439 if (len < 6)
441 *t = 0;
442 if (StrToLong(s, &pen) == -1)
443 pen = 0;
444 D(bug("pen = %ld\n", pen));
445 zc->pen = pen;
446 *t = ']';
448 zc->text = s - 1;
449 zune_text_chunk_new(zc);
450 zc->text_start = t + 1;
451 break;
453 case '-':
454 zc->text += strlenlf(s);
455 break; /* disable engine */
457 default: /* some other ESC code? */
458 if (isdigit(c)) /* pen */
460 zc->dripen = c - '0';
462 break;
464 return zc->text;
467 /**************************************************************************
468 Parse a text line, and create text chunks.
469 Whenever an escape code is encountered, the current chunk
470 is terminated, and the escape parameters are stacked until
471 a normal character is read. Then a new chunk begins with this char.
473 s_ptr is a pointer to the text string. After this function has been
474 executed it is changed to point to the end of the string (either the '\n'
475 or 0 byte)
477 This is probably a function to rewrite to deal with wide chars.
479 Note that the contents in s_ptr is overwritten.
480 **************************************************************************/
481 static ZTextLine *zune_text_parse_line(CONST_STRPTR * s_ptr,
482 struct zune_context *zc, int *argtype, int arg)
484 CONST_STRPTR s;
485 UBYTE c;
487 ZTextLine *ztl;
489 if (!s_ptr)
490 return NULL;
491 if (!(s = *s_ptr))
492 return NULL;
493 if (!(ztl = mui_alloc_struct(ZTextLine)))
494 return NULL;
495 NewList((struct List *)&ztl->chunklist);
497 ztl->align = zc->align;
498 zc->text_start = zc->text = s;
499 zc->line = ztl;
501 while ((c = *s))
503 if (c == '\n')
504 break;
506 if (c == '\33')
508 s++;
509 if (*s == 0)
510 break;
511 s = parse_escape_code(ztl, zc, s);
512 continue;
515 if (*argtype == ZTEXT_ARG_HICHAR && (((arg & 0xff) == c)
516 || (((arg >> 8) & 0xff) == c)))
518 ULONG styleback = zc->style;
519 zune_text_chunk_new(zc);
520 zc->style |= FSF_UNDERLINED;
521 zc->text_start = s;
522 zc->text = ++s;
523 zune_text_chunk_new(zc);
524 zc->text_start = s;
525 zc->style = styleback;
526 *argtype = ZTEXT_ARG_NONE;
527 continue;
530 if (*argtype == ZTEXT_ARG_HICHARIDX && arg == c)
532 ULONG styleback = zc->style;
533 /* underline next char */
534 zune_text_chunk_new(zc);
535 zc->style |= FSF_UNDERLINED;
536 zc->text_start = ++s;
537 zc->text = ++s;
538 zune_text_chunk_new(zc);
539 zc->text_start = s;
540 zc->style = styleback;
541 *argtype = ZTEXT_ARG_NONE;
544 zc->text = ++s;
545 } /* while */
546 zune_text_chunk_new(zc);
547 *s_ptr = s;
548 return ztl;
551 /************************************************************/
552 /* Bounds */
553 /************************************************************/
555 void zune_text_get_bounds(ZText * text, Object * obj)
557 struct RastPort rp;
558 struct TextFont *font;
560 ZTextLine *line_node;
561 ZTextChunk *chunk_node;
563 if (!text || !obj)
564 return;
566 text->width = 0;
567 text->height = 0;
569 font = _font(obj);
570 InitRastPort(&rp);
571 SetFont(&rp, font);
573 for (line_node = (ZTextLine *) text->lines.mlh_Head;
574 line_node->node.mln_Succ;
575 line_node = (ZTextLine *) line_node->node.mln_Succ)
577 line_node->lheight = font->tf_YSize;
578 line_node->lwidth = 0;
580 for (chunk_node = (ZTextChunk *) line_node->chunklist.mlh_Head;
581 chunk_node->node.mln_Succ;
582 chunk_node = (ZTextChunk *) chunk_node->node.mln_Succ)
584 if (chunk_node->spec)
586 struct MUI_MinMax minmax;
588 chunk_node->image =
589 zune_imspec_setup((IPTR) chunk_node->spec,
590 muiRenderInfo(obj));
591 if (!chunk_node->image)
592 return;
593 zune_imspec_askminmax(chunk_node->image, &minmax);
594 chunk_node->cwidth = minmax.DefWidth;
595 chunk_node->cheight = minmax.DefHeight;
596 line_node->lheight =
597 MAX(line_node->lheight, chunk_node->cheight);
599 else if (chunk_node->obj)
601 struct MUI_MinMax MinMax;
603 DoMethod(chunk_node->obj, MUIM_AskMinMax, (IPTR) & MinMax);
604 __area_finish_minmax(chunk_node->obj, &MinMax);
605 chunk_node->cwidth = MinMax.DefWidth;
606 chunk_node->cheight = MinMax.DefHeight;
607 line_node->lheight =
608 MAX(line_node->lheight, chunk_node->cheight);
610 else if (chunk_node->alpha_data)
612 chunk_node->cwidth = chunk_node->alpha_data->width;
613 chunk_node->cheight = chunk_node->alpha_data->height;
614 line_node->lheight =
615 MAX(line_node->lheight, chunk_node->cheight);
617 else if (chunk_node->str)
619 chunk_node->cheight = font->tf_YSize;
620 chunk_node->cwidth =
621 TextLength(&rp, chunk_node->str,
622 strlen(chunk_node->str));
623 D(bug("zune_text_get_bounds(%s,%x) => cwidth=%d\n",
624 chunk_node->str, obj, chunk_node->cwidth));
626 line_node->lwidth += chunk_node->cwidth;
629 text->height += line_node->lheight;
630 text->width = MAX(text->width, line_node->lwidth);
634 /************************************************************/
635 /* Drawing */
636 /************************************************************/
638 // problem is cheight being seldom 0
640 void zune_text_draw(ZText * text, Object * obj, WORD left, WORD right,
641 WORD top)
643 struct RastPort *rp;
644 ULONG pensave;
645 ULONG style = FS_NORMAL;
647 ZTextLine *line_node;
648 ZTextChunk *chunk_node;
650 if (!text || !obj)
651 return;
653 D(bug("zune_text_draw(%p) %d %d %d\n", obj, left, right, top));
655 rp = _rp(obj);
656 SetFont(rp, _font(obj));
657 SetSoftStyle(rp, style, AskSoftStyle(rp));
659 for (line_node = (ZTextLine *) text->lines.mlh_Head;
660 line_node->node.mln_Succ;
661 line_node = (ZTextLine *) line_node->node.mln_Succ)
663 LONG x;
665 if (line_node->align == ZTL_CENTER)
666 x = (left + right + 1 - line_node->lwidth) / 2;
667 else if (line_node->align == ZTL_RIGHT)
668 x = right - line_node->lwidth + 1;
669 else
670 x = left;
672 // like MUI, never truncates the beginning of a line
673 if (x < left)
674 x = left;
676 //D(bug("zune_text_draw(%x,%d,%d,%d, align=%d) : x = %d\n",
677 //obj, left, right, line_node->lwidth, line_node->align, x));
679 for (chunk_node = (ZTextChunk *) line_node->chunklist.mlh_Head;
680 chunk_node->node.mln_Succ;
681 chunk_node = (ZTextChunk *) chunk_node->node.mln_Succ)
684 * Show/Hide stuff should be put in new api calls
686 if (chunk_node->image)
688 WORD top_im = top;
689 top_im += (line_node->lheight - chunk_node->cheight) / 2;
690 zune_imspec_show(chunk_node->image, obj);
691 pensave = GetAPen(rp);
692 zune_imspec_draw(chunk_node->image, muiRenderInfo(obj), x,
693 top_im, chunk_node->cwidth,
694 chunk_node->cheight, 0, 0, 0);
695 SetAPen(rp, pensave);
696 zune_imspec_hide(chunk_node->image);
698 else if (chunk_node->obj)
700 _left(chunk_node->obj) = x;
701 _top(chunk_node->obj) =
702 top + (line_node->lheight - chunk_node->cheight) / 2;
703 _width(chunk_node->obj) = chunk_node->cwidth;
704 _height(chunk_node->obj) = chunk_node->cheight;
705 DoShowMethod(chunk_node->obj);
706 pensave = GetAPen(rp);
707 MUI_Redraw(chunk_node->obj, MADF_DRAWOBJECT);
708 SetAPen(rp, pensave);
709 DoHideMethod(chunk_node->obj);
711 else if (chunk_node->alpha_data)
713 struct MUI_AlphaData *alpha_data = chunk_node->alpha_data;
714 WritePixelArrayAlpha(alpha_data->data, 0, 0,
715 alpha_data->width * 4, rp, x, top, alpha_data->width,
716 alpha_data->height, 0);
718 else if (chunk_node->str)
720 if (chunk_node->style != style)
722 SetSoftStyle(rp, chunk_node->style, 0xff);
723 style = chunk_node->style;
725 if (chunk_node->cheight == 0)
726 Move(rp, x, _font(obj)->tf_Baseline + top);
727 else
728 Move(rp, x,
729 _font(obj)->tf_Baseline + (line_node->lheight -
730 chunk_node->cheight) / 2 + top);
731 #if 0
732 D(bug
733 ("zune_text_draw(%p) Moved to %d (baseline=%d, lh=%d, ch=%d, top=%d)\n",
734 obj,
735 _font(obj)->tf_Baseline + (line_node->lheight -
736 chunk_node->cheight) / 2 + top,
737 _font(obj)->tf_Baseline, line_node->lheight,
738 chunk_node->cheight, top));
739 #endif
740 if (chunk_node->dripen != -1)
742 D(bug("chunk_node->dripen == %d\n",
743 chunk_node->dripen));
744 SetABPenDrMd(rp,
745 _dri(obj)->dri_Pens[chunk_node->dripen], 0, JAM1);
747 else if (chunk_node->pen != -1)
749 D(bug("chunk_node->pen == %d\n", chunk_node->pen));
750 SetABPenDrMd(rp, chunk_node->pen, 0, JAM1);
752 else
754 SetDrMd(rp, JAM1);
756 Text(rp, chunk_node->str, strlen(chunk_node->str));
758 x += chunk_node->cwidth;
760 top += line_node->lheight;