Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / muimaster / textengine.c
blob88fea9acf386195f5684e2cf56d55837e627b084
1 /*
2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002, 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, int argtype, TEXT argbyte);
77 char *zune_text_iso_string(ZText *text);
78 void zune_text_destroy (ZText *text);
79 void zune_text_chunk_new(struct zune_context *zc);
80 static int strlenlf(const char *str);
81 static CONST_STRPTR parse_escape_code (ZTextLine *ztl, struct zune_context *zc, CONST_STRPTR s);
82 static ZTextLine *zune_text_parse_line (CONST_STRPTR *s, struct zune_context *zc,
83 int *argtype, int arg);
84 void zune_text_get_bounds (ZText *text, Object *obj);
85 void zune_text_draw (ZText *text, Object *obj, WORD left, WORD right, WORD top);
86 void zune_text_draw_cursor (ZText *text, Object *obj, WORD left, WORD right, WORD top,
87 LONG cursorx, LONG cursory);
88 void zune_text_draw_single (ZText *text, Object *obj, WORD left, WORD right, WORD top,
89 LONG xpos, LONG ypos, BOOL cursor);
90 int zune_text_get_char_pos(ZText *text, Object *obj, LONG x, LONG y,
91 struct ZTextLine **line_ptr, struct ZTextChunk **chunk_ptr,
92 int *offset_ptr, int *len_ptr);
93 int zune_text_get_line_len(ZText *text, Object *obj, LONG y);
94 int zune_get_xpos_of_line(ZText *text, Object *obj, LONG y, LONG xpixel);
95 int zune_text_get_lines(ZText *text);
96 int zune_make_cursor_visible(ZText *text, Object *obj, LONG cursorx, LONG cursory,
97 LONG left, LONG top, LONG right, LONG bottom);
98 int zune_text_merge(ZText *text, Object *obj, int x, int y, ZText *tomerge);
101 /**************************************************************************
103 **************************************************************************/
104 ZText *zune_text_new (CONST_STRPTR preparse, CONST_STRPTR content, int argtype, TEXT argbyte)
106 ZText *text;
107 /* STRPTR *lines; */
108 /* int i; */
109 STRPTR dup_content;
110 CONST_STRPTR buf;
111 int preparse_len;
112 struct zune_context zc;
113 int arg;
115 if (!(text = mui_alloc_struct(ZText))) return NULL;
116 NewList((struct List*)&text->lines);
118 if (!content) content = "";
119 preparse_len = preparse?strlen(preparse):0;
120 if (!(dup_content = mui_alloc(preparse_len + strlen(content)+1)))
122 mui_free(text);
123 return NULL;
126 if (preparse_len) strcpy(dup_content,preparse);
127 strcpy(&dup_content[preparse_len],content);
129 buf = dup_content;
131 zc.pen = -1;
132 zc.dripen = -1;
133 zc.style = FS_NORMAL;
134 zc.align = ZTL_LEFT;
135 zc.imspec = NULL;
136 zc.obj = NULL;
138 if (argtype == ZTEXT_ARG_HICHAR)
140 /* store lower and upper case in arg */
141 arg = ToLower(argbyte)|(ToUpper(argbyte)<<8);
142 } else arg = argbyte;
144 /* the other elements are done by other functions */
146 while (1)
148 struct ZTextLine *ztl = zune_text_parse_line(&buf, &zc, &argtype, arg);
149 if (ztl) AddTail((struct List*)&text->lines,(struct Node*)ztl);
150 else break;
151 if (*buf == '\n')
153 buf++;
154 continue;
156 if (!(*buf)) break;
159 mui_free(dup_content);
160 return text;
163 /**************************************************************************
164 Completly frees a ZText
165 **************************************************************************/
166 void zune_text_destroy (ZText *text)
168 struct ZTextLine *ztl;
169 struct ZTextChunk *ztc;
171 while ((ztl = (struct ZTextLine *)RemTail((struct List*)&text->lines)))
173 while ((ztc = (struct ZTextChunk*)RemTail((struct List*)&ztl->chunklist)))
175 if (ztc->str) mui_free(ztc->str);
176 if (ztc->spec)
178 FreeVec((APTR)ztc->spec);
179 if (ztc->image)
181 zune_imspec_cleanup(ztc->image);
184 mui_free(ztc);
186 mui_free(ztl);
188 mui_free(text);
191 /************************************************************/
192 /* Parsing */
193 /************************************************************/
196 /**************************************************************************
197 Allocated and initialize a new text chunk and add it to the list
198 The context contains the values for the chunk to be created.
199 **************************************************************************/
200 void zune_text_chunk_new(struct zune_context *zc)
202 ZTextChunk *ztc;
204 /* No char has been processed so we needn't to allocate anything */
205 if (zc->text == zc->text_start) return;
207 if (!(ztc = mui_alloc_struct(ZTextChunk))) return;
209 ztc->style = zc->style;
210 ztc->dripen = zc->dripen;
211 ztc->pen = zc->pen;
212 if (zc->imspec)
214 D(bug("zune_text_chunk_new: imspec %s\n", zc->imspec));
215 ztc->spec = zc->imspec;
216 zc->imspec = NULL;
218 AddTail((struct List*)&zc->line->chunklist,(struct Node*)ztc);
220 else if (zc->obj)
222 ztc->obj = zc->obj;
223 zc->obj = NULL;
225 AddTail((struct List*)&zc->line->chunklist,(struct Node*)ztc);
227 else if ((ztc->str = (char*)mui_alloc(zc->text - zc->text_start + 1)))
229 strncpy(ztc->str, zc->text_start, zc->text - zc->text_start + 1);
230 ztc->str[zc->text - zc->text_start] = 0;
231 D(bug("zune_text_chunk_new: string %s\n", ztc->str));
233 AddTail((struct List*)&zc->line->chunklist,(struct Node*)ztc);
235 else
237 mui_free(ztc);
242 /**************************************************************************
243 Calculates the length of the string exluding line feed or 0 byte
244 **************************************************************************/
245 static int strlenlf(const char *str)
247 char c;
248 int len = 0;
249 while ((c = *str))
251 if (c=='\n') break;
252 len++;
253 str++;
255 return len;
258 /**************************************************************************
259 Note: Only aligments at the beginning of a line should affect this line
260 (tested in MUI)
261 **************************************************************************/
262 static CONST_STRPTR parse_escape_code (ZTextLine *ztl, struct zune_context *zc, CONST_STRPTR s)
264 unsigned char c;
265 c = *s;
267 zune_text_chunk_new(zc);
268 s++; /* s now points after the command */
269 zc->text_start = zc->text = s;
271 switch (c)
273 case 'c': zc->align = ztl->align = ZTL_CENTER; break;
274 case 'r': zc->align = ztl->align = ZTL_RIGHT; break;
275 case 'l': zc->align = ztl->align = ZTL_LEFT; break;
276 case 'n': zc->style = FS_NORMAL; break;
277 case 'u': zc->style |= FSF_UNDERLINED; break;
278 case 'b': zc->style |= FSF_BOLD; break;
279 case 'i': zc->style |= FSF_ITALIC; break;
280 case 'I': /* image spec */
282 char *t;
284 if (*s != '[')
285 break;
286 s++;
287 /* s points on the first char of imagespec.
288 * Extract it to the trailing ']'.
290 t = strchr(s, ']');
291 if (t == NULL)
292 break;
293 *t = 0;
294 D(bug("imspec = %s\n", s));
295 zc->imspec = StrDup(s);
296 *t = ']';
297 zc->text = t;
298 zune_text_chunk_new(zc);
299 zc->text_start = t + 1;
301 case 'O': /* pointer from List_CreateImage */
303 struct ListImage *li;
304 ULONG tmp;
305 char *t;
307 if (*s != '[')
308 break;
309 s++;
310 /* s points on the first char of pointer printed as %08lx.
311 * Extract it to the trailing ']'.
313 t = strchr(s, ']');
314 if (t == NULL)
315 break;
316 *t = 0;
317 if (HexToLong(s,&tmp) != -1)
319 D(bug("listimage = %lx\n", tmp));
320 if (tmp == NULL)
322 /* According to the MUI autodocs, the result of
323 * CreateImage may be NULL, but then \33O[] has to
324 * simply draw nothing, so it shouldn't be considered
325 * an error.
326 * Without this, AROS crashed, if 00000000 was used.
328 *t = ']';
329 zc->text = t+1;
330 zc->text_start=t+1;
331 break;
333 li = (struct ListImage *)tmp;
334 zc->obj = li->obj;
336 *t = ']';
337 zc->text = t;
338 zune_text_chunk_new(zc);
339 zc->text_start = t + 1;
340 break;
342 case 'P': /* pen number */
344 LONG pen;
345 char *t;
347 if (*s != '[')
348 break;
349 s++;
350 /* s points on the first char of pen from ObtainPen printed as %ld.
351 * Extract it to the trailing ']'.
353 t = strchr(s, ']');
354 if (t == NULL)
355 break;
356 *t = 0;
357 if (StrToLong(s,&pen) != -1)
359 D(bug("pen = %ld\n", pen));
360 zc->pen = pen;
362 *t = ']';
363 zc->text = t;
364 zune_text_chunk_new(zc);
365 zc->text_start = t + 1;
366 break;
368 case '-': zc->text += strlenlf(s); break; /* disable engine */
370 default: /* some other ESC code ? */
371 if (isdigit(c)) /* pen */
373 zc->dripen = c - '0';
375 break;
377 return zc->text;
380 /**************************************************************************
381 Parse a text line, and create text chunks.
382 Whenever an escape code is encountered, the current chunk
383 is terminated, and the escape parameters are stacked until
384 a normal character is read. Then a new chunk begins with this char.
386 s_ptr is a pointer to the text string. After this function has been
387 executed it is changed to point to the end of the string (eighter the '\n'
388 or 0 byte)
390 This is probably a function to rewrite to deal with wide chars.
392 Note that the contents in s_ptr is overwritten.
393 **************************************************************************/
394 static ZTextLine *zune_text_parse_line (CONST_STRPTR *s_ptr, struct zune_context *zc, int *argtype, int arg)
396 CONST_STRPTR s;
397 UBYTE c;
399 ZTextLine *ztl;
401 if (!s_ptr) return NULL;
402 if (!(s = *s_ptr)) return NULL;
403 if (!(ztl = mui_alloc_struct(ZTextLine))) return NULL;
404 NewList((struct List*)&ztl->chunklist);
406 ztl->align = zc->align;
407 zc->text_start = zc->text = s;
408 zc->line = ztl;
410 while ((c = *s))
412 if (c == '\n') break;
414 if (c == '\33')
416 s++;
417 if (*s == 0) break;
418 s = parse_escape_code(ztl, zc, s);
419 continue;
422 if (*argtype == ZTEXT_ARG_HICHAR && (((arg & 0xff) == c) || (((arg >> 8)&0xff) == c)))
424 ULONG styleback = zc->style;
425 zune_text_chunk_new(zc);
426 zc->style |= FSF_UNDERLINED;
427 zc->text_start = s;
428 zc->text = ++s;
429 zune_text_chunk_new(zc);
430 zc->text_start = s;
431 zc->style = styleback;
432 *argtype = ZTEXT_ARG_NONE;
433 continue;
436 if (*argtype == ZTEXT_ARG_HICHARIDX && arg == c)
438 ULONG styleback = zc->style;
439 /* underline next char */
440 zune_text_chunk_new(zc);
441 zc->style |= FSF_UNDERLINED;
442 zc->text_start = ++s;
443 zc->text = ++s;
444 zune_text_chunk_new(zc);
445 zc->text_start = s;
446 zc->style = styleback;
447 *argtype = ZTEXT_ARG_NONE;
450 zc->text = ++s;
451 } /* while */
452 zune_text_chunk_new(zc);
453 *s_ptr = s;
454 return ztl;
457 /************************************************************/
458 /* Bounds */
459 /************************************************************/
461 void zune_text_get_bounds (ZText *text, Object *obj)
463 struct RastPort rp;
464 struct TextFont *font;
466 ZTextLine *line_node;
467 ZTextChunk *chunk_node;
469 if (!text || !obj) return;
471 text->width = 0;
472 text->height = 0;
474 font = _font(obj);
475 InitRastPort(&rp);
476 SetFont(&rp, font);
478 for (line_node = (ZTextLine *)text->lines.mlh_Head; line_node->node.mln_Succ ; line_node = (ZTextLine*)line_node->node.mln_Succ)
480 line_node->lheight = font->tf_YSize;
481 line_node->lwidth = 0;
483 for (chunk_node = (ZTextChunk *)line_node->chunklist.mlh_Head; chunk_node->node.mln_Succ ; chunk_node = (ZTextChunk*)chunk_node->node.mln_Succ)
485 if (chunk_node->spec)
487 struct MUI_MinMax minmax;
489 chunk_node->image = zune_imspec_setup((IPTR)chunk_node->spec, muiRenderInfo(obj));
490 if (!chunk_node->image)
491 return;
492 zune_imspec_askminmax(chunk_node->image, &minmax);
493 chunk_node->cwidth = minmax.DefWidth;
494 chunk_node->cheight = minmax.DefHeight;
495 line_node->lheight = MAX(line_node->lheight, chunk_node->cheight);
497 else if (chunk_node->obj)
499 struct MUI_MinMax MinMax;
501 DoMethod(chunk_node->obj, MUIM_AskMinMax, (IPTR)&MinMax);
502 __area_finish_minmax(chunk_node->obj, &MinMax);
503 chunk_node->cwidth = MinMax.DefWidth;
504 chunk_node->cheight = MinMax.DefHeight;
505 line_node->lheight = MAX(line_node->lheight, chunk_node->cheight);
507 else if (chunk_node->str)
509 chunk_node->cheight = font->tf_YSize;
510 chunk_node->cwidth = TextLength(&rp,chunk_node->str,strlen(chunk_node->str));
511 D(bug("zune_text_get_bounds(%s,%x) => cwidth=%d\n", chunk_node->str, obj, chunk_node->cwidth));
513 line_node->lwidth += chunk_node->cwidth;
516 text->height += line_node->lheight;
517 text->width = MAX(text->width,line_node->lwidth);
520 DeinitRastPort(&rp);
523 /************************************************************/
524 /* Drawing */
525 /************************************************************/
527 // problem is cheight being seldom 0
529 void zune_text_draw (ZText *text, Object *obj, WORD left, WORD right, WORD top)
531 struct RastPort *rp;
532 ULONG pensave;
533 ULONG style = FS_NORMAL;
535 ZTextLine *line_node;
536 ZTextChunk *chunk_node;
538 if (!text || !obj) return;
540 D(bug("zune_text_draw(%p) %d %d %d\n", obj, left, right, top));
542 rp = _rp(obj);
543 SetFont(rp,_font(obj));
544 SetSoftStyle(rp, style, AskSoftStyle(rp));
546 for (line_node = (ZTextLine *)text->lines.mlh_Head;
547 line_node->node.mln_Succ ;
548 line_node = (ZTextLine*)line_node->node.mln_Succ)
550 LONG x;
552 if (line_node->align == ZTL_CENTER)
553 x = (left + right + 1 - line_node->lwidth) / 2;
554 else if (line_node->align == ZTL_RIGHT)
555 x = right - line_node->lwidth + 1;
556 else
557 x = left;
559 // like MUI, never truncates the beginning of a line
560 if (x < left) x = left;
562 /* D(bug("zune_text_draw(%x,%d,%d,%d, align=%d) : x = %d\n", obj, left, right, line_node->lwidth, line_node->align, x)); */
564 for (chunk_node = (ZTextChunk *)line_node->chunklist.mlh_Head;
565 chunk_node->node.mln_Succ ;
566 chunk_node = (ZTextChunk*)chunk_node->node.mln_Succ)
569 * Show/Hide stuff should be put in new api calls
571 if (chunk_node->image)
573 WORD top_im = top;
574 top_im += (line_node->lheight - chunk_node->cheight) / 2;
575 zune_imspec_show(chunk_node->image, obj);
576 pensave = GetAPen(rp);
577 zune_imspec_draw(chunk_node->image, muiRenderInfo(obj), x,
578 top_im, chunk_node->cwidth,
579 chunk_node->cheight, 0, 0, 0);
580 SetAPen(rp, pensave);
581 zune_imspec_hide(chunk_node->image);
583 else if (chunk_node->obj)
585 _left(chunk_node->obj) = x;
586 _top(chunk_node->obj) = top + (line_node->lheight - chunk_node->cheight) / 2;
587 _width(chunk_node->obj) = chunk_node->cwidth;
588 _height(chunk_node->obj) = chunk_node->cheight;
589 DoShowMethod(chunk_node->obj);
590 pensave = GetAPen(rp);
591 MUI_Redraw(chunk_node->obj, MADF_DRAWOBJECT);
592 SetAPen(rp, pensave);
593 DoHideMethod(chunk_node->obj);
595 else if (chunk_node->str)
597 if (chunk_node->style != style)
599 SetSoftStyle(rp, chunk_node->style, 0xff);
600 style = chunk_node->style;
602 if (chunk_node->cheight == 0)
603 Move(rp,x,_font(obj)->tf_Baseline + top);
604 else
605 Move(rp,x,_font(obj)->tf_Baseline + (line_node->lheight - chunk_node->cheight) / 2 + top);
606 #if 0
607 D(bug("zune_text_draw(%p) Moved to %d (baseline=%d, lh=%d, ch=%d, top=%d)\n",
608 obj, _font(obj)->tf_Baseline + (line_node->lheight - chunk_node->cheight) / 2 + top,
609 _font(obj)->tf_Baseline, line_node->lheight, chunk_node->cheight, top));
610 #endif
611 if (chunk_node->dripen != -1)
613 D(bug("chunk_node->dripen == %d\n", chunk_node->dripen));
614 SetABPenDrMd(rp, _dri(obj)->dri_Pens[chunk_node->dripen],0,JAM1);
616 else if (chunk_node->pen != -1)
618 D(bug("chunk_node->pen == %d\n", chunk_node->pen));
619 SetABPenDrMd(rp, chunk_node->pen, 0, JAM1);
621 else
623 SetDrMd(rp, JAM1);
625 Text(rp,chunk_node->str,strlen(chunk_node->str));
627 x += chunk_node->cwidth;
629 top += line_node->lheight;