2 Copyright © 1999, David Le Corfec.
3 Copyright © 2002-2013, 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
,
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
,
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
,
104 /**************************************************************************
106 **************************************************************************/
107 ZText
*zune_text_new(CONST_STRPTR preparse
, CONST_STRPTR content
,
108 int argtype
, TEXT argbyte
)
116 struct zune_context zc
;
119 if (!(text
= mui_alloc_struct(ZText
)))
121 NewList((struct List
*)&text
->lines
);
125 preparse_len
= preparse
? strlen(preparse
) : 0;
126 if (!(dup_content
= mui_alloc(preparse_len
+ strlen(content
) + 1)))
133 strcpy(dup_content
, preparse
);
134 strcpy(&dup_content
[preparse_len
], content
);
140 zc
.style
= FS_NORMAL
;
145 if (argtype
== ZTEXT_ARG_HICHAR
)
147 /* store lower and upper case in arg */
148 arg
= ToLower(argbyte
) | (ToUpper(argbyte
) << 8);
153 /* the other elements are done by other functions */
157 struct ZTextLine
*ztl
=
158 zune_text_parse_line(&buf
, &zc
, &argtype
, arg
);
160 AddTail((struct List
*)&text
->lines
, (struct Node
*)ztl
);
172 mui_free(dup_content
);
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
)))
187 (struct ZTextChunk
*)RemTail((struct List
*)&ztl
->
194 FreeVec((APTR
) ztc
->spec
);
197 zune_imspec_cleanup(ztc
->image
);
207 /************************************************************/
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
)
220 /* No char has been processed so we needn't allocate anything */
221 if (zc
->text
== zc
->text_start
)
224 if (!(ztc
= mui_alloc_struct(ZTextChunk
)))
227 ztc
->style
= zc
->style
;
228 ztc
->dripen
= zc
->dripen
;
232 D(bug("zune_text_chunk_new: imspec %s\n", zc
->imspec
));
233 ztc
->spec
= zc
->imspec
;
236 AddTail((struct List
*)&zc
->line
->chunklist
, (struct Node
*)ztc
);
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
);
260 /**************************************************************************
261 Calculates the length of the string excluding line feed or 0 byte
262 **************************************************************************/
263 static int strlenlf(const char *str
)
277 /**************************************************************************
278 Note: Only alignments at the beginning of a line should affect this line
280 **************************************************************************/
281 static CONST_STRPTR
parse_escape_code(ZTextLine
* ztl
,
282 struct zune_context
*zc
, CONST_STRPTR s
)
287 zune_text_chunk_new(zc
);
288 s
++; /* s now points after the command */
289 zc
->text_start
= zc
->text
= s
;
294 zc
->align
= ztl
->align
= ZTL_CENTER
;
297 zc
->align
= ztl
->align
= ZTL_RIGHT
;
300 zc
->align
= ztl
->align
= ZTL_LEFT
;
303 zc
->style
= FS_NORMAL
;
306 zc
->style
|= FSF_UNDERLINED
;
309 zc
->style
|= FSF_BOLD
;
312 zc
->style
|= FSF_ITALIC
;
314 case 'I': /* image spec */
321 /* s points on the first char of imagespec.
322 * Extract it to the trailing ']'.
328 D(bug("imspec = %s\n", s
));
329 zc
->imspec
= StrDup(s
);
332 zune_text_chunk_new(zc
);
333 zc
->text_start
= t
+ 1;
336 case 'O': /* pointer from List_CreateImage */
338 struct ListImage
*li
;
345 /* s points on the first char of pointer printed as %08lx.
346 * Extract it to the trailing ']'.
352 if (HexToIPTR(s
, &tmp
) != -1)
354 D(bug("listimage = %lx\n", tmp
));
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
361 * Without this, AROS crashed, if 00000000 was used.
365 zc
->text_start
= t
+ 1;
368 li
= (struct ListImage
*)tmp
;
373 zune_text_chunk_new(zc
);
374 zc
->text_start
= t
+ 1;
377 case 'P': /* pen number */
385 /* s points on the first char of pen from ObtainPen printed as %ld.
386 * Extract it to the trailing ']'.
392 if (StrToLong(s
, &pen
) != -1)
394 D(bug("pen = %ld\n", pen
));
399 zune_text_chunk_new(zc
);
400 zc
->text_start
= t
+ 1;
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';
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'
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
)
443 if (!(ztl
= mui_alloc_struct(ZTextLine
)))
445 NewList((struct List
*)&ztl
->chunklist
);
447 ztl
->align
= zc
->align
;
448 zc
->text_start
= zc
->text
= s
;
461 s
= parse_escape_code(ztl
, zc
, s
);
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
;
473 zune_text_chunk_new(zc
);
475 zc
->style
= styleback
;
476 *argtype
= ZTEXT_ARG_NONE
;
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
;
488 zune_text_chunk_new(zc
);
490 zc
->style
= styleback
;
491 *argtype
= ZTEXT_ARG_NONE
;
496 zune_text_chunk_new(zc
);
501 /************************************************************/
503 /************************************************************/
505 void zune_text_get_bounds(ZText
* text
, Object
* obj
)
508 struct TextFont
*font
;
510 ZTextLine
*line_node
;
511 ZTextChunk
*chunk_node
;
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
;
539 zune_imspec_setup((IPTR
) chunk_node
->spec
,
541 if (!chunk_node
->image
)
543 zune_imspec_askminmax(chunk_node
->image
, &minmax
);
544 chunk_node
->cwidth
= minmax
.DefWidth
;
545 chunk_node
->cheight
= minmax
.DefHeight
;
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
;
558 MAX(line_node
->lheight
, chunk_node
->cheight
);
560 else if (chunk_node
->str
)
562 chunk_node
->cheight
= font
->tf_YSize
;
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 /************************************************************/
579 /************************************************************/
581 // problem is cheight being seldom 0
583 void zune_text_draw(ZText
* text
, Object
* obj
, WORD left
, WORD right
,
588 ULONG style
= FS_NORMAL
;
590 ZTextLine
*line_node
;
591 ZTextChunk
*chunk_node
;
596 D(bug("zune_text_draw(%p) %d %d %d\n", obj
, left
, right
, top
));
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
)
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;
615 // like MUI, never truncates the beginning of a line
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
)
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
);
665 _font(obj
)->tf_Baseline
+ (line_node
->lheight
-
666 chunk_node
->cheight
) / 2 + top
);
669 ("zune_text_draw(%p) Moved to %d (baseline=%d, lh=%d, ch=%d, top=%d)\n",
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
));
676 if (chunk_node
->dripen
!= -1)
678 D(bug("chunk_node->dripen == %d\n",
679 chunk_node
->dripen
));
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
);
692 Text(rp
, chunk_node
->str
, strlen(chunk_node
->str
));
694 x
+= chunk_node
->cwidth
;
696 top
+= line_node
->lheight
;