2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002-2018, 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>
25 #include <proto/cybergraphics.h>
28 #include "textengine.h"
31 #include "listimage.h"
33 #include "muimaster_intern.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.
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 ?
71 CONST_STRPTR text_start
;
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
,
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
,
106 /**************************************************************************
108 **************************************************************************/
109 ZText
*zune_text_new(CONST_STRPTR preparse
, CONST_STRPTR content
,
110 int argtype
, TEXT argbyte
)
118 struct zune_context zc
;
121 if (!(text
= mui_alloc_struct(ZText
)))
123 NewList((struct List
*)&text
->lines
);
127 preparse_len
= preparse
? strlen(preparse
) : 0;
128 if (!(dup_content
= mui_alloc(preparse_len
+ strlen(content
) + 1)))
135 strcpy(dup_content
, preparse
);
136 strcpy(&dup_content
[preparse_len
], content
);
142 zc
.style
= FS_NORMAL
;
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);
156 /* the other elements are done by other functions */
160 struct ZTextLine
*ztl
=
161 zune_text_parse_line(&buf
, &zc
, &argtype
, arg
);
163 AddTail((struct List
*)&text
->lines
, (struct Node
*)ztl
);
175 mui_free(dup_content
);
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
)))
190 (struct ZTextChunk
*)RemTail((struct List
*)&ztl
->
197 FreeVec((APTR
) ztc
->spec
);
200 zune_imspec_cleanup(ztc
->image
);
210 /************************************************************/
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
)
223 /* No char has been processed so we needn't allocate anything */
224 if (zc
->text
== zc
->text_start
)
227 if (!(ztc
= mui_alloc_struct(ZTextChunk
)))
230 ztc
->style
= zc
->style
;
231 ztc
->dripen
= zc
->dripen
;
235 D(bug("zune_text_chunk_new: imspec %s\n", zc
->imspec
));
236 ztc
->spec
= zc
->imspec
;
239 AddTail((struct List
*)&zc
->line
->chunklist
, (struct Node
*)ztc
);
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
);
270 /**************************************************************************
271 Calculates the length of the string excluding line feed or 0 byte
272 **************************************************************************/
273 static int strlenlf(const char *str
)
287 /**************************************************************************
288 Note: Only alignments at the beginning of a line should affect this line
290 **************************************************************************/
291 static CONST_STRPTR
parse_escape_code(ZTextLine
* ztl
,
292 struct zune_context
*zc
, CONST_STRPTR s
)
297 zune_text_chunk_new(zc
);
298 s
++; /* s now points after the command */
299 zc
->text_start
= zc
->text
= s
;
304 zc
->align
= ztl
->align
= ZTL_CENTER
;
307 zc
->align
= ztl
->align
= ZTL_RIGHT
;
310 zc
->align
= ztl
->align
= ZTL_LEFT
;
313 zc
->style
= FS_NORMAL
;
316 zc
->style
|= FSF_UNDERLINED
;
319 zc
->style
|= FSF_BOLD
;
322 zc
->style
|= FSF_ITALIC
;
324 case 'I': /* image spec */
331 /* s points on the first char of imagespec.
332 * Extract it to the trailing ']'.
338 D(bug("imspec = %s\n", s
));
339 zc
->imspec
= StrDup(s
);
342 zune_text_chunk_new(zc
);
343 zc
->text_start
= t
+ 1;
346 case 'O': /* pointer from List_CreateImage */
348 struct ListImage
*li
;
355 /* s points on the first char of pointer printed as %08lx.
356 * Extract it to the trailing ']'.
362 if (HexToIPTR(s
, &tmp
) != -1)
364 D(bug("listimage = %lx\n", tmp
));
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
371 * Without this, AROS crashed, if 00000000 was used.
375 zc
->text_start
= t
+ 1;
378 li
= (struct ListImage
*)tmp
;
383 zune_text_chunk_new(zc
);
384 zc
->text_start
= t
+ 1;
387 case 'A': /* pointer to MUI_AlphaData */
395 /* s points on the first char of pointer printed as %08lx.
396 * Extract it to the trailing ']'.
402 if (HexToIPTR(s
, &tmp
) != -1)
404 D(bug("image = %lx\n", tmp
));
407 /* Draw nothing if image pointer is NULL, as MUI 4 does
408 * (although undocumented)
412 zc
->text_start
= t
+ 1;
415 zc
->alpha_data
= (struct MUI_AlphaData
*)tmp
;
419 zune_text_chunk_new(zc
);
420 zc
->text_start
= t
+ 1;
423 case 'P': /* pen number */
432 /* s points on the first char of pen from ObtainPen printed as %ld.
433 * Extract it to the trailing ']'.
442 if (StrToLong(s
, &pen
) == -1)
444 D(bug("pen = %ld\n", pen
));
449 zune_text_chunk_new(zc
);
450 zc
->text_start
= t
+ 1;
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';
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'
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
)
493 if (!(ztl
= mui_alloc_struct(ZTextLine
)))
495 NewList((struct List
*)&ztl
->chunklist
);
497 ztl
->align
= zc
->align
;
498 zc
->text_start
= zc
->text
= s
;
511 s
= parse_escape_code(ztl
, zc
, s
);
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
;
523 zune_text_chunk_new(zc
);
525 zc
->style
= styleback
;
526 *argtype
= ZTEXT_ARG_NONE
;
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
;
538 zune_text_chunk_new(zc
);
540 zc
->style
= styleback
;
541 *argtype
= ZTEXT_ARG_NONE
;
546 zune_text_chunk_new(zc
);
551 /************************************************************/
553 /************************************************************/
555 void zune_text_get_bounds(ZText
* text
, Object
* obj
)
558 struct TextFont
*font
;
560 ZTextLine
*line_node
;
561 ZTextChunk
*chunk_node
;
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
;
589 zune_imspec_setup((IPTR
) chunk_node
->spec
,
591 if (!chunk_node
->image
)
593 zune_imspec_askminmax(chunk_node
->image
, &minmax
);
594 chunk_node
->cwidth
= minmax
.DefWidth
;
595 chunk_node
->cheight
= minmax
.DefHeight
;
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
;
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
;
615 MAX(line_node
->lheight
, chunk_node
->cheight
);
617 else if (chunk_node
->str
)
619 chunk_node
->cheight
= font
->tf_YSize
;
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 /************************************************************/
636 /************************************************************/
638 // problem is cheight being seldom 0
640 void zune_text_draw(ZText
* text
, Object
* obj
, WORD left
, WORD right
,
645 ULONG style
= FS_NORMAL
;
647 ZTextLine
*line_node
;
648 ZTextChunk
*chunk_node
;
653 D(bug("zune_text_draw(%p) %d %d %d\n", obj
, left
, right
, top
));
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
)
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;
672 // like MUI, never truncates the beginning of a line
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
)
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
);
729 _font(obj
)->tf_Baseline
+ (line_node
->lheight
-
730 chunk_node
->cheight
) / 2 + top
);
733 ("zune_text_draw(%p) Moved to %d (baseline=%d, lh=%d, ch=%d, top=%d)\n",
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
));
740 if (chunk_node
->dripen
!= -1)
742 D(bug("chunk_node->dripen == %d\n",
743 chunk_node
->dripen
));
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
);
756 Text(rp
, chunk_node
->str
, strlen(chunk_node
->str
));
758 x
+= chunk_node
->cwidth
;
760 top
+= line_node
->lheight
;