grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / libs / muimaster / textengine.c
blob303aa060ab679b70b0a9c333097be8ceacf76010
1 /*
2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002-2013, 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>
26 #include "mui.h"
27 #include "textengine.h"
28 #include "imspec.h"
29 #include "support.h"
30 #include "listimage.h"
32 #include "muimaster_intern.h"
34 //#define MYDEBUG 1
35 #include "debug.h"
37 extern struct Library *MUIMasterBase;
39 /* A bit of explanation:
40 * The most important thing, after the datastructure, is the bounds
41 * calculation (especially chunk width).
42 * It will determine where to draw. Drawing is then straightforward.
43 * From the beginning:
44 * the input string is parsed line by line. Each line is formed of chunks
45 * of symbols having the same rendering style. As a special case,
46 * HiChar is a single letter chunk with underline style.
47 * Size calculation is done by calculating for each line the width and
48 * height of each chunk, then line width is the sum of widths and line height
49 * is the max height. *Text width will be max line width, and text height
50 * will be the sum of line heights.* Remember group layout.
51 * Chunk width is affected by soft style of text, and maybe other params.
52 * Drawing is done line by line, chunk by chunk, incrementing each time
53 * the offsets with chunk width or line height.
55 * Italic is looking ugly, no ?
58 #define ZTL_LEFT 1
59 #define ZTL_CENTER 2
60 #define ZTL_RIGHT 3
62 struct zune_context
64 LONG dripen;
65 LONG pen;
66 ULONG style;
67 UBYTE align;
69 ZTextLine *line;
70 CONST_STRPTR text_start;
71 CONST_STRPTR text;
72 CONST_STRPTR imspec;
73 Object *obj; /* Area subclass, see List_CreateImage */
76 ZText *zune_text_new(CONST_STRPTR preparse, CONST_STRPTR content,
77 int argtype, TEXT argbyte);
78 char *zune_text_iso_string(ZText * text);
79 void zune_text_destroy(ZText * text);
80 void zune_text_chunk_new(struct zune_context *zc);
81 static int strlenlf(const char *str);
82 static CONST_STRPTR parse_escape_code(ZTextLine * ztl,
83 struct zune_context *zc, CONST_STRPTR s);
84 static ZTextLine *zune_text_parse_line(CONST_STRPTR * s,
85 struct zune_context *zc, int *argtype, int arg);
86 void zune_text_get_bounds(ZText * text, Object * obj);
87 void zune_text_draw(ZText * text, Object * obj, WORD left, WORD right,
88 WORD top);
89 void zune_text_draw_cursor(ZText * text, Object * obj, WORD left,
90 WORD right, WORD top, LONG cursorx, LONG cursory);
91 void zune_text_draw_single(ZText * text, Object * obj, WORD left,
92 WORD right, WORD top, LONG xpos, LONG ypos, BOOL cursor);
93 int zune_text_get_char_pos(ZText * text, Object * obj, LONG x, LONG y,
94 struct ZTextLine **line_ptr, struct ZTextChunk **chunk_ptr,
95 int *offset_ptr, int *len_ptr);
96 int zune_get_xpos_of_line(ZText * text, Object * obj, LONG y, LONG xpixel);
97 int zune_text_get_lines(ZText * text);
98 int zune_make_cursor_visible(ZText * text, Object * obj, LONG cursorx,
99 LONG cursory, LONG left, LONG top, LONG right, LONG bottom);
100 int zune_text_merge(ZText * text, Object * obj, int x, int y,
101 ZText * tomerge);
104 /**************************************************************************
106 **************************************************************************/
107 ZText *zune_text_new(CONST_STRPTR preparse, CONST_STRPTR content,
108 int argtype, TEXT argbyte)
110 ZText *text;
111 /* STRPTR *lines; */
112 /* int i; */
113 STRPTR dup_content;
114 CONST_STRPTR buf;
115 int preparse_len;
116 struct zune_context zc;
117 int arg;
119 if (!(text = mui_alloc_struct(ZText)))
120 return NULL;
121 NewList((struct List *)&text->lines);
123 if (!content)
124 content = "";
125 preparse_len = preparse ? strlen(preparse) : 0;
126 if (!(dup_content = mui_alloc(preparse_len + strlen(content) + 1)))
128 mui_free(text);
129 return NULL;
132 if (preparse_len)
133 strcpy(dup_content, preparse);
134 strcpy(&dup_content[preparse_len], content);
136 buf = dup_content;
138 zc.pen = -1;
139 zc.dripen = -1;
140 zc.style = FS_NORMAL;
141 zc.align = ZTL_LEFT;
142 zc.imspec = NULL;
143 zc.obj = NULL;
145 if (argtype == ZTEXT_ARG_HICHAR)
147 /* store lower and upper case in arg */
148 arg = ToLower(argbyte) | (ToUpper(argbyte) << 8);
150 else
151 arg = argbyte;
153 /* the other elements are done by other functions */
155 while (1)
157 struct ZTextLine *ztl =
158 zune_text_parse_line(&buf, &zc, &argtype, arg);
159 if (ztl)
160 AddTail((struct List *)&text->lines, (struct Node *)ztl);
161 else
162 break;
163 if (*buf == '\n')
165 buf++;
166 continue;
168 if (!(*buf))
169 break;
172 mui_free(dup_content);
173 return text;
176 /**************************************************************************
177 Completely frees a ZText
178 **************************************************************************/
179 void zune_text_destroy(ZText * text)
181 struct ZTextLine *ztl;
182 struct ZTextChunk *ztc;
184 while ((ztl = (struct ZTextLine *)RemTail((struct List *)&text->lines)))
186 while ((ztc =
187 (struct ZTextChunk *)RemTail((struct List *)&ztl->
188 chunklist)))
190 if (ztc->str)
191 mui_free(ztc->str);
192 if (ztc->spec)
194 FreeVec((APTR) ztc->spec);
195 if (ztc->image)
197 zune_imspec_cleanup(ztc->image);
200 mui_free(ztc);
202 mui_free(ztl);
204 mui_free(text);
207 /************************************************************/
208 /* Parsing */
209 /************************************************************/
212 /**************************************************************************
213 Allocate and initialize a new text chunk and add it to the list.
214 The context contains the values for the chunk to be created.
215 **************************************************************************/
216 void zune_text_chunk_new(struct zune_context *zc)
218 ZTextChunk *ztc;
220 /* No char has been processed so we needn't allocate anything */
221 if (zc->text == zc->text_start)
222 return;
224 if (!(ztc = mui_alloc_struct(ZTextChunk)))
225 return;
227 ztc->style = zc->style;
228 ztc->dripen = zc->dripen;
229 ztc->pen = zc->pen;
230 if (zc->imspec)
232 D(bug("zune_text_chunk_new: imspec %s\n", zc->imspec));
233 ztc->spec = zc->imspec;
234 zc->imspec = NULL;
236 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
238 else if (zc->obj)
240 ztc->obj = zc->obj;
241 zc->obj = NULL;
243 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
245 else if ((ztc->str = (char *)mui_alloc(zc->text - zc->text_start + 1)))
247 strncpy(ztc->str, zc->text_start, zc->text - zc->text_start + 1);
248 ztc->str[zc->text - zc->text_start] = 0;
249 D(bug("zune_text_chunk_new: string %s\n", ztc->str));
251 AddTail((struct List *)&zc->line->chunklist, (struct Node *)ztc);
253 else
255 mui_free(ztc);
260 /**************************************************************************
261 Calculates the length of the string excluding line feed or 0 byte
262 **************************************************************************/
263 static int strlenlf(const char *str)
265 char c;
266 int len = 0;
267 while ((c = *str))
269 if (c == '\n')
270 break;
271 len++;
272 str++;
274 return len;
277 /**************************************************************************
278 Note: Only alignments at the beginning of a line should affect this line
279 (tested in MUI)
280 **************************************************************************/
281 static CONST_STRPTR parse_escape_code(ZTextLine * ztl,
282 struct zune_context *zc, CONST_STRPTR s)
284 unsigned char c;
285 c = *s;
287 zune_text_chunk_new(zc);
288 s++; /* s now points after the command */
289 zc->text_start = zc->text = s;
291 switch (c)
293 case 'c':
294 zc->align = ztl->align = ZTL_CENTER;
295 break;
296 case 'r':
297 zc->align = ztl->align = ZTL_RIGHT;
298 break;
299 case 'l':
300 zc->align = ztl->align = ZTL_LEFT;
301 break;
302 case 'n':
303 zc->style = FS_NORMAL;
304 break;
305 case 'u':
306 zc->style |= FSF_UNDERLINED;
307 break;
308 case 'b':
309 zc->style |= FSF_BOLD;
310 break;
311 case 'i':
312 zc->style |= FSF_ITALIC;
313 break;
314 case 'I': /* image spec */
316 char *t;
318 if (*s != '[')
319 break;
320 s++;
321 /* s points on the first char of imagespec.
322 * Extract it to the trailing ']'.
324 t = strchr(s, ']');
325 if (t == NULL)
326 break;
327 *t = 0;
328 D(bug("imspec = %s\n", s));
329 zc->imspec = StrDup(s);
330 *t = ']';
331 zc->text = t;
332 zune_text_chunk_new(zc);
333 zc->text_start = t + 1;
334 break;
336 case 'O': /* pointer from List_CreateImage */
338 struct ListImage *li;
339 IPTR tmp;
340 char *t;
342 if (*s != '[')
343 break;
344 s++;
345 /* s points on the first char of pointer printed as %08lx.
346 * Extract it to the trailing ']'.
348 t = strchr(s, ']');
349 if (t == NULL)
350 break;
351 *t = 0;
352 if (HexToIPTR(s, &tmp) != -1)
354 D(bug("listimage = %lx\n", tmp));
355 if (tmp == 0)
357 /* According to the MUI autodocs, the result of
358 * CreateImage may be NULL, but then \33O[] has to
359 * simply draw nothing, so it shouldn't be considered
360 * an error.
361 * Without this, AROS crashed, if 00000000 was used.
363 *t = ']';
364 zc->text = t + 1;
365 zc->text_start = t + 1;
366 break;
368 li = (struct ListImage *)tmp;
369 zc->obj = li->obj;
371 *t = ']';
372 zc->text = t;
373 zune_text_chunk_new(zc);
374 zc->text_start = t + 1;
375 break;
377 case 'P': /* pen number */
379 LONG pen;
380 char *t;
382 if (*s != '[')
383 break;
384 s++;
385 /* s points on the first char of pen from ObtainPen printed as %ld.
386 * Extract it to the trailing ']'.
388 t = strchr(s, ']');
389 if (t == NULL)
390 break;
391 *t = 0;
392 if (StrToLong(s, &pen) != -1)
394 D(bug("pen = %ld\n", pen));
395 zc->pen = pen;
397 *t = ']';
398 zc->text = t;
399 zune_text_chunk_new(zc);
400 zc->text_start = t + 1;
401 break;
403 case '-':
404 zc->text += strlenlf(s);
405 break; /* disable engine */
407 default: /* some other ESC code ? */
408 if (isdigit(c)) /* pen */
410 zc->dripen = c - '0';
412 break;
414 return zc->text;
417 /**************************************************************************
418 Parse a text line, and create text chunks.
419 Whenever an escape code is encountered, the current chunk
420 is terminated, and the escape parameters are stacked until
421 a normal character is read. Then a new chunk begins with this char.
423 s_ptr is a pointer to the text string. After this function has been
424 executed it is changed to point to the end of the string (either the '\n'
425 or 0 byte)
427 This is probably a function to rewrite to deal with wide chars.
429 Note that the contents in s_ptr is overwritten.
430 **************************************************************************/
431 static ZTextLine *zune_text_parse_line(CONST_STRPTR * s_ptr,
432 struct zune_context *zc, int *argtype, int arg)
434 CONST_STRPTR s;
435 UBYTE c;
437 ZTextLine *ztl;
439 if (!s_ptr)
440 return NULL;
441 if (!(s = *s_ptr))
442 return NULL;
443 if (!(ztl = mui_alloc_struct(ZTextLine)))
444 return NULL;
445 NewList((struct List *)&ztl->chunklist);
447 ztl->align = zc->align;
448 zc->text_start = zc->text = s;
449 zc->line = ztl;
451 while ((c = *s))
453 if (c == '\n')
454 break;
456 if (c == '\33')
458 s++;
459 if (*s == 0)
460 break;
461 s = parse_escape_code(ztl, zc, s);
462 continue;
465 if (*argtype == ZTEXT_ARG_HICHAR && (((arg & 0xff) == c)
466 || (((arg >> 8) & 0xff) == c)))
468 ULONG styleback = zc->style;
469 zune_text_chunk_new(zc);
470 zc->style |= FSF_UNDERLINED;
471 zc->text_start = s;
472 zc->text = ++s;
473 zune_text_chunk_new(zc);
474 zc->text_start = s;
475 zc->style = styleback;
476 *argtype = ZTEXT_ARG_NONE;
477 continue;
480 if (*argtype == ZTEXT_ARG_HICHARIDX && arg == c)
482 ULONG styleback = zc->style;
483 /* underline next char */
484 zune_text_chunk_new(zc);
485 zc->style |= FSF_UNDERLINED;
486 zc->text_start = ++s;
487 zc->text = ++s;
488 zune_text_chunk_new(zc);
489 zc->text_start = s;
490 zc->style = styleback;
491 *argtype = ZTEXT_ARG_NONE;
494 zc->text = ++s;
495 } /* while */
496 zune_text_chunk_new(zc);
497 *s_ptr = s;
498 return ztl;
501 /************************************************************/
502 /* Bounds */
503 /************************************************************/
505 void zune_text_get_bounds(ZText * text, Object * obj)
507 struct RastPort rp;
508 struct TextFont *font;
510 ZTextLine *line_node;
511 ZTextChunk *chunk_node;
513 if (!text || !obj)
514 return;
516 text->width = 0;
517 text->height = 0;
519 font = _font(obj);
520 InitRastPort(&rp);
521 SetFont(&rp, font);
523 for (line_node = (ZTextLine *) text->lines.mlh_Head;
524 line_node->node.mln_Succ;
525 line_node = (ZTextLine *) line_node->node.mln_Succ)
527 line_node->lheight = font->tf_YSize;
528 line_node->lwidth = 0;
530 for (chunk_node = (ZTextChunk *) line_node->chunklist.mlh_Head;
531 chunk_node->node.mln_Succ;
532 chunk_node = (ZTextChunk *) chunk_node->node.mln_Succ)
534 if (chunk_node->spec)
536 struct MUI_MinMax minmax;
538 chunk_node->image =
539 zune_imspec_setup((IPTR) chunk_node->spec,
540 muiRenderInfo(obj));
541 if (!chunk_node->image)
542 return;
543 zune_imspec_askminmax(chunk_node->image, &minmax);
544 chunk_node->cwidth = minmax.DefWidth;
545 chunk_node->cheight = minmax.DefHeight;
546 line_node->lheight =
547 MAX(line_node->lheight, chunk_node->cheight);
549 else if (chunk_node->obj)
551 struct MUI_MinMax MinMax;
553 DoMethod(chunk_node->obj, MUIM_AskMinMax, (IPTR) & MinMax);
554 __area_finish_minmax(chunk_node->obj, &MinMax);
555 chunk_node->cwidth = MinMax.DefWidth;
556 chunk_node->cheight = MinMax.DefHeight;
557 line_node->lheight =
558 MAX(line_node->lheight, chunk_node->cheight);
560 else if (chunk_node->str)
562 chunk_node->cheight = font->tf_YSize;
563 chunk_node->cwidth =
564 TextLength(&rp, chunk_node->str,
565 strlen(chunk_node->str));
566 D(bug("zune_text_get_bounds(%s,%x) => cwidth=%d\n",
567 chunk_node->str, obj, chunk_node->cwidth));
569 line_node->lwidth += chunk_node->cwidth;
572 text->height += line_node->lheight;
573 text->width = MAX(text->width, line_node->lwidth);
577 /************************************************************/
578 /* Drawing */
579 /************************************************************/
581 // problem is cheight being seldom 0
583 void zune_text_draw(ZText * text, Object * obj, WORD left, WORD right,
584 WORD top)
586 struct RastPort *rp;
587 ULONG pensave;
588 ULONG style = FS_NORMAL;
590 ZTextLine *line_node;
591 ZTextChunk *chunk_node;
593 if (!text || !obj)
594 return;
596 D(bug("zune_text_draw(%p) %d %d %d\n", obj, left, right, top));
598 rp = _rp(obj);
599 SetFont(rp, _font(obj));
600 SetSoftStyle(rp, style, AskSoftStyle(rp));
602 for (line_node = (ZTextLine *) text->lines.mlh_Head;
603 line_node->node.mln_Succ;
604 line_node = (ZTextLine *) line_node->node.mln_Succ)
606 LONG x;
608 if (line_node->align == ZTL_CENTER)
609 x = (left + right + 1 - line_node->lwidth) / 2;
610 else if (line_node->align == ZTL_RIGHT)
611 x = right - line_node->lwidth + 1;
612 else
613 x = left;
615 // like MUI, never truncates the beginning of a line
616 if (x < left)
617 x = left;
619 //D(bug("zune_text_draw(%x,%d,%d,%d, align=%d) : x = %d\n",
620 //obj, left, right, line_node->lwidth, line_node->align, x));
622 for (chunk_node = (ZTextChunk *) line_node->chunklist.mlh_Head;
623 chunk_node->node.mln_Succ;
624 chunk_node = (ZTextChunk *) chunk_node->node.mln_Succ)
627 * Show/Hide stuff should be put in new api calls
629 if (chunk_node->image)
631 WORD top_im = top;
632 top_im += (line_node->lheight - chunk_node->cheight) / 2;
633 zune_imspec_show(chunk_node->image, obj);
634 pensave = GetAPen(rp);
635 zune_imspec_draw(chunk_node->image, muiRenderInfo(obj), x,
636 top_im, chunk_node->cwidth,
637 chunk_node->cheight, 0, 0, 0);
638 SetAPen(rp, pensave);
639 zune_imspec_hide(chunk_node->image);
641 else if (chunk_node->obj)
643 _left(chunk_node->obj) = x;
644 _top(chunk_node->obj) =
645 top + (line_node->lheight - chunk_node->cheight) / 2;
646 _width(chunk_node->obj) = chunk_node->cwidth;
647 _height(chunk_node->obj) = chunk_node->cheight;
648 DoShowMethod(chunk_node->obj);
649 pensave = GetAPen(rp);
650 MUI_Redraw(chunk_node->obj, MADF_DRAWOBJECT);
651 SetAPen(rp, pensave);
652 DoHideMethod(chunk_node->obj);
654 else if (chunk_node->str)
656 if (chunk_node->style != style)
658 SetSoftStyle(rp, chunk_node->style, 0xff);
659 style = chunk_node->style;
661 if (chunk_node->cheight == 0)
662 Move(rp, x, _font(obj)->tf_Baseline + top);
663 else
664 Move(rp, x,
665 _font(obj)->tf_Baseline + (line_node->lheight -
666 chunk_node->cheight) / 2 + top);
667 #if 0
668 D(bug
669 ("zune_text_draw(%p) Moved to %d (baseline=%d, lh=%d, ch=%d, top=%d)\n",
670 obj,
671 _font(obj)->tf_Baseline + (line_node->lheight -
672 chunk_node->cheight) / 2 + top,
673 _font(obj)->tf_Baseline, line_node->lheight,
674 chunk_node->cheight, top));
675 #endif
676 if (chunk_node->dripen != -1)
678 D(bug("chunk_node->dripen == %d\n",
679 chunk_node->dripen));
680 SetABPenDrMd(rp,
681 _dri(obj)->dri_Pens[chunk_node->dripen], 0, JAM1);
683 else if (chunk_node->pen != -1)
685 D(bug("chunk_node->pen == %d\n", chunk_node->pen));
686 SetABPenDrMd(rp, chunk_node->pen, 0, JAM1);
688 else
690 SetDrMd(rp, JAM1);
692 Text(rp, chunk_node->str, strlen(chunk_node->str));
694 x += chunk_node->cwidth;
696 top += line_node->lheight;