revert between 56095 -> 55830 in arch
[AROS.git] / workbench / libs / muimaster / classes / floattext.c
blob92fcf437cb3029a22cabf1b4d25dfeb8b261cc80
1 /*
2 Copyright © 2002-2015, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #define MUIMASTER_YES_INLINE_STDARG
8 #define DEBUG 0
9 #include <aros/debug.h>
11 #include <exec/memory.h>
12 #include <clib/alib_protos.h>
13 #include <proto/exec.h>
14 #include <proto/dos.h>
15 #include <proto/intuition.h>
16 #include <proto/utility.h>
17 #include <proto/muimaster.h>
18 #include <proto/graphics.h>
20 #include <string.h>
22 #include "mui.h"
23 #include "muimaster_intern.h"
24 #include "support.h"
25 #include "support_classes.h"
26 #include "floattext_private.h"
28 extern struct Library *MUIMasterBase;
30 // like strlen(), but \n ends string, too.
31 static long MyStrLen(const char *ptr)
33 const char *start = ptr;
35 while (*ptr && (*ptr != '\n'))
36 ptr++;
38 return (((long)ptr) - ((long)start));
42 * Calculates the number of characters that will fit on a line of a given
43 * pixel width.
45 static UWORD FitParagraphLine(STRPTR text, ULONG length, WORD width,
46 ULONG offset, struct Window *window)
48 struct TextExtent temp_extent;
49 ULONG char_count;
50 UBYTE *p, *q = text + offset;
52 char_count = TextFit(window->RPort, text + offset,
53 length - offset, &temp_extent, NULL, 1, width, 32767);
55 /* Find the last space in the fitted substring */
57 if (offset + char_count != length)
59 for (p = text + offset + char_count; p > q && *p != ' '; p--);
60 for (; p > q && *p == ' '; p--);
61 if (p > q)
62 char_count = p - q + 1;
65 return char_count;
68 static void SetText(Object *obj, struct Floattext_DATA *data)
70 WORD width;
71 struct Window *window;
72 UWORD i, count, pos, line_size = 0, space_count = 0, extra_space_count,
73 space_width, space_multiple = 0, bonus_space_count = 0, line_len,
74 bonus_space_mod = 1, space_no_in = 0, space_no_out, stripped_pos,
75 stripped_count, stripped_size = 0, control_count, old_control_count,
76 tab_count;
77 UBYTE *text, *p, *q, *r, c, *line = NULL, *old_line,
78 *stripped_text = NULL;
79 LONG len, stripped_len;
80 BOOL justify, found, is_empty;
82 /* Avoid redrawing list while inserting lines */
84 data->typesetting = TRUE;
86 DoMethod(obj, MUIM_List_Clear);
88 window = (struct Window *)XGET(obj, MUIA_Window);
89 width = XGET(obj, MUIA_Width) - XGET(obj, MUIA_InnerLeft)
90 - XGET(obj, MUIA_InnerRight) - 8;
92 /* Only do layout if we have some text and a window in which to put it */
94 if (data->text && window != NULL)
96 /* Get width of a space character */
98 space_width = TextLength(window->RPort, " ", 1);
100 /* Lay out each paragraph */
102 for (text = data->text; text[0] != '\0'; text += pos)
104 stripped_pos = pos = 0;
105 len = MyStrLen(text);
107 if (len > 0)
109 /* Allocate paragraph buffer (the buffer size assumes the
110 * worst-case scenario: every character is a tab) */
112 if (stripped_text == NULL
113 || len * data->tabsize >= stripped_size)
115 FreeVec(stripped_text);
116 stripped_size = len * data->tabsize + 1;
117 stripped_text = AllocVec(stripped_size, MEMF_ANY);
119 if (stripped_text == NULL)
121 FreeVec(line);
122 return;
125 /* Make a copy of paragraph text without control sequences
126 * or skip-chars, and with tabs expanded to spaces */
128 for (p = text, q = stripped_text; *p != '\0' && *p != '\n'; )
130 if (*p == '\33')
131 p += 2;
132 else if (*p == '\t')
134 for (i = 0; i < data->tabsize; i++)
135 *q++ = ' ';
136 p++;
138 else if (data->skipchars != NULL)
140 for (r = data->skipchars; *r != '\0' && *r != *p; r++);
141 if (*r == '\0')
142 *q++ = *p;
143 p++;
145 else
146 *q++ = *p++;
148 *q = '\0';
149 stripped_len = MyStrLen(stripped_text);
151 /* Reuse old line buffer, but don't carry over control codes
152 * into new paragraph */
154 if (line != NULL)
155 line[0] = '\0';
156 old_control_count = control_count = 0;
158 /* Divide this paragraph into lines */
160 while ((stripped_count = FitParagraphLine(stripped_text,
161 stripped_len, width, stripped_pos, window)) != 0)
163 /* Count number of characters for this line in original
164 * text */
166 old_control_count += control_count;
167 control_count = tab_count = 0;
168 for (i = 0, p = text + pos; i < stripped_count
169 || (i == stripped_count && *p != ' ' && *p != '\t');
170 p++)
172 if (*p == '\33')
174 control_count += 2;
175 p++;
177 else if (*p == '\t')
179 control_count++;
180 tab_count++;
181 i += data->tabsize;
183 else if (data->skipchars != NULL)
185 for (r = data->skipchars;
186 *r != '\0' && *r != *p; r++);
187 if (*r != '\0' || (*p == ' ' && i == 0))
188 control_count++;
189 else
190 i++;
192 else
193 i++;
195 count = stripped_count + control_count
196 - tab_count * data->tabsize;
198 /* Default to justified text if it's enabled and this
199 * isn't the last line of the paragraph */
201 justify = data->justify;
202 if (justify)
204 /* Count number of spaces in stripped line */
206 p = stripped_text + stripped_pos;
207 for (i = 0, space_count = 0; i < stripped_count; i++)
209 if (p[i] == ' ')
210 space_count++;
212 space_count -= tab_count * data->tabsize;
214 if (space_count == 0)
215 justify = FALSE;
218 if (justify)
220 /* Find out how many extra spaces to insert for fully
221 * justified text */
223 extra_space_count = (width - TextLength(window->RPort,
224 stripped_text + stripped_pos, stripped_count))
225 / space_width;
227 space_multiple = (space_count + extra_space_count)
228 / space_count;
229 bonus_space_count = (space_count + extra_space_count)
230 - space_count * space_multiple;
231 if (bonus_space_count > 0)
232 bonus_space_mod =
233 (space_count + 3) / bonus_space_count;
234 else
235 bonus_space_mod = space_count;
237 /* Don't justify on last line if it will be too
238 * stretched */
240 if (space_multiple > 5
241 && pos + count == len)
242 justify = FALSE;
244 else
245 extra_space_count = 0;
247 /* Count number of characters in line that will be
248 * inserted into List object */
250 line_len = old_control_count + control_count
251 + stripped_count
252 + tab_count * (data->tabsize - 1) + extra_space_count;
254 /* Allocate line buffer (we allocate more space than
255 * necessary to reduce the need to reallocate if later
256 * lines are longer) */
258 old_line = line;
259 if (line == NULL || line_len >= line_size)
261 line_size = line_len * 2 + 1;
262 line = AllocVec(line_size, MEMF_ANY);
264 if (line == NULL)
266 FreeVec(old_line);
267 FreeVec(stripped_text);
268 return;
271 /* Prefix new line with all control characters contained
272 * in previous line */
274 q = line;
275 if (old_line != NULL)
277 for (p = old_line; *p != '\0'; p++)
279 if (*p == '\33')
281 *q++ = *p++;
282 *q++ = *p;
286 if (old_line != line)
287 FreeVec(old_line);
289 /* Generate line to insert in List object */
291 is_empty = TRUE;
292 p = text + pos;
293 c = p[count];
294 p[count] = '\0';
295 space_no_in = space_no_out = 0;
296 while (*p != '\0')
298 if (*p == ' ' && justify && !is_empty)
300 /* Add extra spaces to justify text */
302 for (i = 0; i < space_multiple; i++)
303 *q++ = ' ';
304 space_no_out += space_multiple;
306 if (bonus_space_count > 0
307 && (space_no_in % bonus_space_mod == 0))
309 *q++ = ' ';
310 bonus_space_count--;
312 space_no_out++;
314 p++;
315 space_no_in++;
317 /* Last input space? Make it as wide as
318 * necessary for line to reach right border */
320 if (space_no_in == space_count)
322 while (bonus_space_count-- > 0)
323 *q++ = ' ';
326 else if (*p == '\t')
328 /* Expand tab to spaces */
330 for (i = 0; i < data->tabsize; i++)
331 *q++ = ' ';
332 p++;
334 else if (data->skipchars != NULL)
336 /* Filter out skip-chars */
338 for (r = data->skipchars;
339 *r != '\0' && *r != *p; r++);
340 if (*r == '\0' && !(*p == ' ' && is_empty))
342 *q++ = *p;
343 is_empty = FALSE;
345 p++;
347 else
349 /* No skip-chars, so direct copy */
351 *q++ = *p++;
352 is_empty = FALSE;
355 *q = '\0';
357 /* Add line to list */
359 DoMethod(obj, MUIM_List_InsertSingle, line,
360 MUIV_List_Insert_Bottom);
361 *p = c;
363 /* Move on to first word of next line */
365 stripped_pos += stripped_count;
366 pos += count;
367 if (pos != len)
369 if (data->skipchars != NULL)
371 for (found = FALSE, p = text + pos;
372 !found; pos++, p++)
374 for (r = data->skipchars;
375 *r != '\0' && *r != *p; r++);
376 if (*r == '\0' && *p != ' ' && *p != '\t')
377 found = TRUE;
379 pos--, p--;
381 else
382 for (p = text + pos; *p == ' ' || *p == '\t';
383 pos++, p++);
384 for (p = stripped_text + stripped_pos; *(p++) == ' ';
385 stripped_pos++);
389 else
390 DoMethod(obj, MUIM_List_InsertSingle, "",
391 MUIV_List_Insert_Bottom);
393 if (text[pos] == '\n')
394 pos++;
396 FreeVec(line);
397 FreeVec(stripped_text);
400 data->typesetting = FALSE;
402 DoMethod(obj, MUIM_List_Redraw, MUIV_List_Redraw_All);
405 IPTR Floattext__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
407 struct Floattext_DATA *data;
408 struct TagItem *tag;
409 struct TagItem *tags;
411 obj = (Object *) DoSuperMethodA(cl, obj, (Msg) msg);
413 if (!obj)
415 return 0;
418 data = INST_DATA(cl, obj);
420 SetAttrs(obj, MUIA_List_ConstructHook, MUIV_List_ConstructHook_String,
421 MUIA_List_DestructHook, MUIV_List_DestructHook_String, TAG_DONE);
423 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
425 switch (tag->ti_Tag)
427 case MUIA_Floattext_Justify:
428 data->justify = tag->ti_Data;
429 break;
431 case MUIA_Floattext_SkipChars:
432 data->skipchars = (STRPTR) tag->ti_Data;
433 break;
435 case MUIA_Floattext_TabSize:
436 data->tabsize = tag->ti_Data;
437 break;
439 case MUIA_Floattext_Text:
440 data->text = StrDup((STRPTR) tag->ti_Data);
441 break;
445 if (data->tabsize == 0)
446 data->tabsize = 8;
447 else if (data->tabsize > 20)
448 data->tabsize = 20;
450 SetText(obj, data);
452 return (IPTR) obj;
455 IPTR Floattext__OM_DISPOSE(struct IClass *cl, Object *obj,
456 struct opSet *msg)
458 struct Floattext_DATA *data = INST_DATA(cl, obj);
460 FreeVec(data->text);
462 return DoSuperMethodA(cl, obj, (Msg) msg);
465 IPTR Floattext__OM_GET(struct IClass *cl, Object *obj, struct opGet *msg)
467 struct Floattext_DATA *data = INST_DATA(cl, obj);
468 #define STORE *(msg->opg_Storage)
470 switch (msg->opg_AttrID)
472 case MUIA_Floattext_Justify:
473 STORE = data->justify;
474 return 1;
476 case MUIA_Floattext_Text:
477 STORE = (IPTR) data->text;
478 return 1;
482 #undef STORE
484 return DoSuperMethodA(cl, obj, (Msg) msg);
487 IPTR Floattext__OM_SET(struct IClass *cl, Object *obj, struct opSet *msg)
489 struct Floattext_DATA *data = INST_DATA(cl, obj);
490 struct TagItem *tag;
491 struct TagItem *tags;
492 BOOL changed = FALSE;
494 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags));)
496 switch (tag->ti_Tag)
498 case MUIA_Floattext_Justify:
499 data->justify = tag->ti_Data != 0;
500 changed = TRUE;
501 break;
503 case MUIA_Floattext_SkipChars:
504 data->skipchars = (STRPTR) tag->ti_Data;
505 changed = TRUE;
506 break;
508 case MUIA_Floattext_TabSize:
509 data->tabsize = tag->ti_Data;
510 changed = TRUE;
511 break;
513 case MUIA_Floattext_Text:
514 FreeVec(data->text);
515 data->text = StrDup((STRPTR) tag->ti_Data);
516 changed = TRUE;
517 break;
522 if (changed) // To avoid recursion
524 if (data->tabsize == 0)
525 data->tabsize = 8;
526 else if (data->tabsize > 20)
527 data->tabsize = 20;
529 SetText(obj, data);
532 return DoSuperMethodA(cl, obj, (Msg) msg);
535 /**************************************************************************
536 MUIM_Draw
537 **************************************************************************/
538 IPTR Floattext__MUIM_Draw(struct IClass *cl, Object *obj,
539 struct MUIP_Draw *msg)
541 struct Floattext_DATA *data = INST_DATA(cl, obj);
543 if (!data->typesetting)
544 DoSuperMethodA(cl, obj, (Msg) msg);
546 if ((msg->flags & MADF_DRAWOBJECT) != 0 && data->oldwidth != _width(obj))
548 data->oldwidth = _width(obj);
549 SetText(obj, data);
552 return 0;
555 /**************************************************************************
556 MUIM_Floattext_Append
557 **************************************************************************/
558 IPTR Floattext__MUIM_Floattext_Append(struct IClass *cl, Object *obj,
559 struct MUIP_Floattext_Append *msg)
561 struct Floattext_DATA *data = INST_DATA(cl, obj);
563 if (msg->Text)
565 ULONG newlen = strlen(msg->Text) + 1;
566 if (data->text)
568 newlen += strlen(data->text);
570 TEXT *newtext = AllocVec(newlen, MEMF_ANY);
571 if (newtext)
573 newtext[0] = '\0';
574 if (data->text)
576 strcpy(newtext, data->text);
578 strcat(newtext, msg->Text);
579 FreeVec(data->text);
580 data->text = newtext;
581 SetText(obj, data);
585 return 0;
588 #if ZUNE_BUILTIN_FLOATTEXT
589 BOOPSI_DISPATCHER(IPTR, Floattext_Dispatcher, cl, obj, msg)
591 switch (msg->MethodID)
593 case OM_NEW:
594 return Floattext__OM_NEW(cl, obj, msg);
595 case OM_DISPOSE:
596 return Floattext__OM_DISPOSE(cl, obj, msg);
597 case OM_GET:
598 return Floattext__OM_GET(cl, obj, msg);
599 case OM_SET:
600 return Floattext__OM_SET(cl, obj, msg);
601 case MUIM_Draw:
602 return Floattext__MUIM_Draw(cl, obj, (struct MUIP_Draw *)msg);
603 case MUIM_Floattext_Append:
604 return Floattext__MUIM_Floattext_Append(cl, obj,
605 (struct MUIP_Floattext_Append *)msg);
607 default:
608 return DoSuperMethodA(cl, obj, msg);
611 BOOPSI_DISPATCHER_END
613 const struct __MUIBuiltinClass _MUI_Floattext_desc =
615 MUIC_Floattext,
616 MUIC_List,
617 sizeof(struct Floattext_DATA),
618 (void *) Floattext_Dispatcher
620 #endif /* ZUNE_BUILTIN_FLOATTEXT */