2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002, The AROS Development Team.
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>
27 #include "textengine.h"
30 #include "listimage.h"
32 #include "muimaster_intern.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.
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 ?
70 CONST_STRPTR text_start
;
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
)
112 struct zune_context zc
;
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)))
126 if (preparse_len
) strcpy(dup_content
,preparse
);
127 strcpy(&dup_content
[preparse_len
],content
);
133 zc
.style
= FS_NORMAL
;
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 */
148 struct ZTextLine
*ztl
= zune_text_parse_line(&buf
, &zc
, &argtype
, arg
);
149 if (ztl
) AddTail((struct List
*)&text
->lines
,(struct Node
*)ztl
);
159 mui_free(dup_content
);
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
);
178 FreeVec((APTR
)ztc
->spec
);
181 zune_imspec_cleanup(ztc
->image
);
191 /************************************************************/
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
)
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
;
214 D(bug("zune_text_chunk_new: imspec %s\n", zc
->imspec
));
215 ztc
->spec
= zc
->imspec
;
218 AddTail((struct List
*)&zc
->line
->chunklist
,(struct Node
*)ztc
);
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
);
242 /**************************************************************************
243 Calculates the length of the string exluding line feed or 0 byte
244 **************************************************************************/
245 static int strlenlf(const char *str
)
258 /**************************************************************************
259 Note: Only aligments at the beginning of a line should affect this line
261 **************************************************************************/
262 static CONST_STRPTR
parse_escape_code (ZTextLine
*ztl
, struct zune_context
*zc
, CONST_STRPTR s
)
267 zune_text_chunk_new(zc
);
268 s
++; /* s now points after the command */
269 zc
->text_start
= zc
->text
= s
;
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 */
287 /* s points on the first char of imagespec.
288 * Extract it to the trailing ']'.
294 D(bug("imspec = %s\n", s
));
295 zc
->imspec
= StrDup(s
);
298 zune_text_chunk_new(zc
);
299 zc
->text_start
= t
+ 1;
301 case 'O': /* pointer from List_CreateImage */
303 struct ListImage
*li
;
310 /* s points on the first char of pointer printed as %08lx.
311 * Extract it to the trailing ']'.
317 if (HexToLong(s
,&tmp
) != -1)
319 D(bug("listimage = %lx\n", tmp
));
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
326 * Without this, AROS crashed, if 00000000 was used.
333 li
= (struct ListImage
*)tmp
;
338 zune_text_chunk_new(zc
);
339 zc
->text_start
= t
+ 1;
342 case 'P': /* pen number */
350 /* s points on the first char of pen from ObtainPen printed as %ld.
351 * Extract it to the trailing ']'.
357 if (StrToLong(s
,&pen
) != -1)
359 D(bug("pen = %ld\n", pen
));
364 zune_text_chunk_new(zc
);
365 zc
->text_start
= t
+ 1;
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';
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'
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
)
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
;
412 if (c
== '\n') break;
418 s
= parse_escape_code(ztl
, zc
, s
);
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
;
429 zune_text_chunk_new(zc
);
431 zc
->style
= styleback
;
432 *argtype
= ZTEXT_ARG_NONE
;
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
;
444 zune_text_chunk_new(zc
);
446 zc
->style
= styleback
;
447 *argtype
= ZTEXT_ARG_NONE
;
452 zune_text_chunk_new(zc
);
457 /************************************************************/
459 /************************************************************/
461 void zune_text_get_bounds (ZText
*text
, Object
*obj
)
464 struct TextFont
*font
;
466 ZTextLine
*line_node
;
467 ZTextChunk
*chunk_node
;
469 if (!text
|| !obj
) return;
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
)
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
);
523 /************************************************************/
525 /************************************************************/
527 // problem is cheight being seldom 0
529 void zune_text_draw (ZText
*text
, Object
*obj
, WORD left
, WORD right
, WORD top
)
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
));
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
)
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;
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
)
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
);
605 Move(rp
,x
,_font(obj
)->tf_Baseline
+ (line_node
->lheight
- chunk_node
->cheight
) / 2 + top
);
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
));
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
);
625 Text(rp
,chunk_node
->str
,strlen(chunk_node
->str
));
627 x
+= chunk_node
->cwidth
;
629 top
+= line_node
->lheight
;