1 /*
2 * Motif
3 *
4 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5 *
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
16 * details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
22 *
23 */
24 /*
25 * HISTORY
26 */
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31
32 #ifdef REV_INFO
33 #ifndef lint
34 static char rcsid[] = "$TOG: XmStringFunc.c /main/9 1999/10/14 11:20:50 mgreess $"
35 #endif
36 #endif
37
38 #include <Xm/XmosP.h>
39
40 #include "XmI.h"
41 #include "XmStringI.h"
42 #include "XmRenderTI.h"
43 #include "XmTabListI.h"
44
45 /******** Static Function Declarations ********/
46
47 static void new_line(_XmString string) ;
48
49 /******** End Static Function Declarations ********/
50
51
52 XmStringComponentType
XmStringPeekNextTriple(XmStringContext context)53 XmStringPeekNextTriple(XmStringContext context)
54 {
55 unsigned int len;
56 XtPointer val;
57
58 return XmeStringGetComponent((_XmStringContext) context, False, False, &len, &val);
59 }
60
61 Boolean
XmStringHasSubstring(XmString string,XmString substring)62 XmStringHasSubstring(
63 XmString string,
64 XmString substring )
65 {
66 _XmStringContextRec stack_context;
67 char *text;
68 char *subtext;
69 short char_count;
70 short subchar_count;
71 Boolean found;
72 int i, j, max;
73 _XmStringEntry line, *entry, seg;
74 XmStringComponentType type;
75 unsigned int len;
76 XtPointer val;
77
78 _XmProcessLock();
79 if ((string == NULL) || (substring == NULL) || (XmStringEmpty(substring))) {
80 _XmProcessUnlock();
81 return (FALSE);
82 }
83
84 /*
85 * The substring must be a one line/one segment string.
86 */
87
88 if (_XmStrEntryCountGet(substring) != 1) {
89 _XmProcessUnlock();
90 return (FALSE);
91 }
92
93 if (((entry = _XmStrEntryGet(substring)) != NULL) &&
94 _XmEntrySegmentCountGet(entry[0]) > 1) {
95 _XmProcessUnlock();
96 return (FALSE);
97 }
98
99 /*
100 * Get the text out of the substring.
101 */
102
103 if (_XmStrOptimized(substring))
104 {
105 subchar_count = (short)_XmStrByteCount(substring);
106 subtext = (char *)_XmStrText(substring);
107 }
108 else if (_XmStrMultiple(substring))
109 {
110 line = entry[0];
111
112 if (_XmEntryMultiple(line))
113 {
114 seg = (_XmStringEntry)_XmEntrySegmentGet(line)[0];
115
116 subchar_count = (short)_XmEntryByteCountGet(seg);
117 subtext = (char*) _XmEntryTextGet(seg);
118 }
119 else
120 {
121 subchar_count = (short)_XmEntryByteCountGet(line);
122 subtext = (char*) _XmEntryTextGet(line);
123 }
124 }
125 else { /* Oops, some weird string! */
126 _XmProcessUnlock();
127 return (FALSE);
128 }
129
130
131 if ((subchar_count == 0) || (subtext == NULL)) {
132 _XmProcessUnlock();
133 return (FALSE);
134 }
135
136 /** Find a text component that matches. **/
137 if (string) {
138 _XmStringContextReInit(&stack_context, string);
139 while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
140 &len, &val)) !=
141 XmSTRING_COMPONENT_END)
142 {
143 switch(type)
144 {
145 case XmSTRING_COMPONENT_TEXT:
146 case XmSTRING_COMPONENT_LOCALE_TEXT:
147 case XmSTRING_COMPONENT_WIDECHAR_TEXT:
148
149 char_count = len;
150 text = (char *)val;
151
152 if (char_count >= subchar_count) {
153 max = char_count - subchar_count;
154 for (i = 0; i <= max; i++) {
155 found = TRUE;
156
157 for (j = 0; j < subchar_count; j++) {
158 if (text[i+j] != subtext[j]) {
159 found = FALSE;
160 break;
161 }
162 }
163 if (found) {
164 _XmStringContextFree(&stack_context);
165 _XmProcessUnlock();
166 return(TRUE);
167 }
168 }
169 }
170 break;
171 default:
172 break;
173 }
174 }
175 _XmStringContextFree(&stack_context);
176 }
177 _XmProcessUnlock();
178 return (FALSE);
179 }
180
181 XmStringTable
XmStringTableParseStringArray(XtPointer * strings,Cardinal count,XmStringTag tag,XmTextType type,XmParseTable parse,Cardinal parse_count,XtPointer call_data)182 XmStringTableParseStringArray(XtPointer *strings,
183 Cardinal count,
184 XmStringTag tag,
185 XmTextType type,
186 XmParseTable parse,
187 Cardinal parse_count,
188 XtPointer call_data)
189 {
190 int i;
191 XmStringTable strs;
192
193 _XmProcessLock();
194 if ((strings == NULL) || (count == 0)) {
195 _XmProcessUnlock();
196 return(NULL);
197 }
198
199 strs = (XmStringTable)XtMalloc(count * sizeof(XmString));
200
201 for (i = 0; i < count; i++)
202 {
203 strs[i] = XmStringParseText(strings[i], NULL, tag, type,
204 parse, parse_count, call_data);
205 }
206
207 _XmProcessUnlock();
208 return(strs);
209 }
210
211 XtPointer *
XmStringTableUnparse(XmStringTable table,Cardinal count,XmStringTag tag,XmTextType tag_type,XmTextType output_type,XmParseTable parse,Cardinal parse_count,XmParseModel parse_model)212 XmStringTableUnparse(XmStringTable table,
213 Cardinal count,
214 XmStringTag tag,
215 XmTextType tag_type,
216 XmTextType output_type,
217 XmParseTable parse,
218 Cardinal parse_count,
219 XmParseModel parse_model)
220 {
221 XtPointer *strs;
222 int i;
223
224 _XmProcessLock();
225 if ((table == NULL) || (count == 0)) {
226 _XmProcessUnlock();
227 return(NULL);
228 }
229
230 strs = (XtPointer *)XtMalloc(count * sizeof(XtPointer));
231
232 for (i = 0; i < count; i++)
233 strs[i] = XmStringUnparse(table[i], tag, tag_type, output_type,
234 parse, parse_count, parse_model);
235 _XmProcessUnlock();
236 return(strs);
237 }
238
239 XmString
XmStringTableToXmString(XmStringTable table,Cardinal count,XmString break_comp)240 XmStringTableToXmString(XmStringTable table,
241 Cardinal count,
242 XmString break_comp)
243 {
244 /* Note: this is a very expensive way to do this. Fix for Beta */
245 int i;
246 XmString str = NULL, tmp1, tmp2;
247
248 _XmProcessLock();
249 tmp1 = NULL;
250
251 for (i = 0; i < count; i++)
252 {
253 tmp2 = XmStringConcatAndFree(tmp1, XmStringCopy(table[i]));
254 str = XmStringConcatAndFree(tmp2, XmStringCopy(break_comp));
255
256 tmp1 = str;
257 }
258
259 _XmProcessUnlock();
260 return(str);
261 }
262
263 XmString
XmStringPutRendition(XmString string,XmStringTag rendition)264 XmStringPutRendition(XmString string,
265 XmStringTag rendition)
266 {
267 /* Quick and dirty. Fix for beta! */
268 XmString str, tmp1, tmp2;
269
270 tmp1 = XmStringComponentCreate(XmSTRING_COMPONENT_RENDITION_BEGIN,
271 strlen(rendition), (XtPointer)rendition);
272 tmp2 = XmStringConcatAndFree(tmp1, XmStringCopy(string));
273
274 tmp1 = XmStringComponentCreate(XmSTRING_COMPONENT_RENDITION_END,
275 strlen(rendition), (XtPointer)rendition);
276 str = XmStringConcatAndFree(tmp2, tmp1);
277
278 return(str);
279 }
280
281 void
XmParseMappingGetValues(XmParseMapping mapping,ArgList arg_list,Cardinal arg_count)282 XmParseMappingGetValues(XmParseMapping mapping,
283 ArgList arg_list,
284 Cardinal arg_count)
285 {
286 register Cardinal i;
287 register String arg_name;
288
289 _XmProcessLock();
290 /* Do a little error checking. */
291 if (mapping == NULL) {
292 _XmProcessUnlock();
293 return;
294 }
295
296 /* Modify the specified values. */
297 for (i = 0; i < arg_count; i++)
298 {
299 arg_name = arg_list[i].name;
300
301 if ((arg_name == XmNpattern) ||
302 (strcmp(arg_name, XmNpattern) == 0))
303 *((XtPointer*)arg_list[i].value) = mapping->pattern;
304 else if ((arg_name == XmNpatternType) ||
305 (strcmp(arg_name, XmNpatternType) == 0))
306 *((XmTextType*)arg_list[i].value) = mapping->pattern_type;
307 else if ((arg_name == XmNsubstitute) ||
308 (strcmp(arg_name, XmNsubstitute) == 0))
309 *((XmString*)arg_list[i].value) = XmStringCopy(mapping->substitute);
310 else if ((arg_name == XmNinvokeParseProc) ||
311 (strcmp(arg_name, XmNinvokeParseProc) == 0))
312 *((XmParseProc*)arg_list[i].value) = mapping->parse_proc;
313 else if ((arg_name == XmNclientData) ||
314 (strcmp(arg_name, XmNclientData) == 0))
315 *((XtPointer*)arg_list[i].value) = mapping->client_data;
316 else if ((arg_name == XmNincludeStatus) ||
317 (strcmp(arg_name, XmNincludeStatus) == 0))
318 *((XmIncludeStatus*)arg_list[i].value) = mapping->include_status;
319 }
320 _XmProcessUnlock();
321 }
322
323 void
XmParseMappingFree(XmParseMapping mapping)324 XmParseMappingFree(XmParseMapping mapping)
325 {
326 _XmProcessLock();
327 if (mapping != NULL)
328 {
329 /* Free copied data. */
330 XmStringFree(mapping->substitute);
331
332 /* Free the record. */
333 XtFree((char*) mapping);
334 }
335 _XmProcessUnlock();
336 }
337
338 void
XmParseTableFree(XmParseTable parse_table,Cardinal parse_count)339 XmParseTableFree(XmParseTable parse_table,
340 Cardinal parse_count)
341 {
342 /* Free each entry in the table. */
343 Cardinal i;
344
345 _XmProcessLock();
346 for (i = 0; i < parse_count; i++)
347 XmParseMappingFree(parse_table[i]);
348
349 /* Free the table itself. */
350 XtFree((char*) parse_table);
351 _XmProcessUnlock();
352 }
353
354 /*
355 * XmeGetNextCharacter: An XmParseProc to consume the triggering
356 * character and insert the following character.
357 */
358 /*ARGSUSED*/
359 XmIncludeStatus
XmeGetNextCharacter(XtPointer * in_out,XtPointer text_end,XmTextType type,XmStringTag tag,XmParseMapping entry,int pattern_length,XmString * str_include,XtPointer call_data)360 XmeGetNextCharacter(XtPointer *in_out,
361 XtPointer text_end,
362 XmTextType type,
363 XmStringTag tag,
364 XmParseMapping entry, /* unused */
365 int pattern_length,
366 XmString *str_include,
367 XtPointer call_data) /* unused */
368 {
369 char* ptr = (char*) *in_out;
370 int len = 0;
371 XmStringComponentType comp_type;
372 assert(in_out != NULL);
373
374 _XmProcessLock();
375 /* Initialize the out parameters */
376 *str_include = NULL;
377
378 /* Consume the triggering characters. */
379 ptr += pattern_length;
380
381 /* Select the component type. */
382 switch (type)
383 {
384 case XmCHARSET_TEXT:
385 if ((tag != NULL) && (strcmp(XmFONTLIST_DEFAULT_TAG, tag) == 0))
386 comp_type = XmSTRING_COMPONENT_LOCALE_TEXT;
387 else
388 comp_type = XmSTRING_COMPONENT_TEXT;
389 if ((text_end == NULL) || (ptr < (char*) text_end))
390 #ifndef NO_MULTIBYTE
391 len = mblen(ptr, MB_CUR_MAX);
392 #else
393 len = *ptr ? 1 : 0;
394 #endif
395 break;
396
397 case XmMULTIBYTE_TEXT:
398 /* In Motif 2.0 dynamic switching of locales isn't supported. */
399 comp_type = XmSTRING_COMPONENT_LOCALE_TEXT;
400 if ((text_end == NULL) || (ptr < (char*) text_end))
401 #ifndef NO_MULTIBYTE
402 len = mblen(ptr, MB_CUR_MAX);
403 #else
404 len = *ptr ? 1 : 0;
405 #endif
406 break;
407
408 case XmWIDECHAR_TEXT:
409 comp_type = XmSTRING_COMPONENT_WIDECHAR_TEXT;
410 if ((text_end == NULL) || (ptr < (char*) text_end))
411 len = sizeof(wchar_t);
412 break;
413
414 default:
415 comp_type = XmSTRING_COMPONENT_UNKNOWN;
416 break;
417 }
418
419 /* Quit if mblen() failed or if type was unrecognized. */
420 if ((len <= 0) || (comp_type == XmSTRING_COMPONENT_UNKNOWN))
421 {
422 *in_out = (XtPointer) ptr;
423 _XmProcessUnlock();
424 return XmINSERT;
425 }
426
427 /* Create a component containing the next character. */
428 *str_include = XmStringComponentCreate(comp_type, len, ptr);
429 ptr += len;
430 *in_out = (XtPointer) ptr;
431
432 _XmProcessUnlock();
433 return XmINSERT;
434 }
435
436 static void
new_line(_XmString string)437 new_line(
438 _XmString string )
439 {
440 int lc = _XmStrEntryCount(string);
441 _XmStringEntry line;
442
443 _XmStrImplicitLine(string) = TRUE;
444
445 _XmStrEntry(string) = (_XmStringEntry *)
446 XtRealloc((char *) _XmStrEntry(string),
447 sizeof(_XmStringEntry) * (lc + 1));
448
449 _XmEntryCreate(line, XmSTRING_ENTRY_ARRAY);
450 _XmStrEntry(string)[lc] = line;
451
452 _XmEntrySegmentCount(line) = 0;
453 _XmEntrySegment(line) = NULL;
454
455 _XmStrEntryCount(string)++;
456 }
457
458 static XmString
MakeStrFromSeg(XmStringContext start)459 MakeStrFromSeg(XmStringContext start)
460 {
461 _XmStringEntry *line;
462 _XmStringEntry *segs, seg;
463 _XmString str;
464
465 if (_XmStrContOpt(start)) {
466 _XmStrContError(start) = TRUE;
467 return(XmStringCopy(_XmStrContString(start)));
468 } else {
469 /* get segment */
470 line = _XmStrEntry(_XmStrContString(start));
471
472 /* Create XmString structure */
473 _XmStrCreate(str, XmSTRING_MULTIPLE_ENTRY, 0);
474
475 if (_XmEntryMultiple(line[_XmStrContCurrLine(start)])) {
476 segs = (_XmStringEntry*)_XmEntrySegment(line[_XmStrContCurrLine(start)]);
477
478 new_line(str);
479
480 if (_XmStrContCurrSeg(start) < _XmEntrySegmentCount(line)) {
481 seg = segs[_XmStrContCurrSeg(start)];
482
483 _XmStringSegmentNew(str, 0, seg, True);
484
485 _XmStrContCurrSeg(start)++;
486
487 _XmStrContDir(start) = _XmEntryDirectionGet(seg);
488 _XmStrContTag(start) = _XmEntryTag(seg);
489 _XmStrContTagType(start) = (XmTextType) _XmEntryTextTypeGet(seg);
490 } else {
491 new_line(str);
492 _XmStrContCurrSeg(start) = 0;
493 _XmStrContCurrLine(start)++;
494 }
495 } else {
496 seg = line[_XmStrContCurrLine(start)];
497 _XmStringSegmentNew(str, 0, seg, True);
498
499 _XmStrContCurrSeg(start) = 0;
500 _XmStrContCurrLine(start)++;
501
502 _XmStrContDir(start) = _XmEntryDirectionGet(seg);
503 _XmStrContTag(start) = _XmEntryTag(seg);
504 _XmStrContTagType(start) = (XmTextType) _XmEntryTextTypeGet(seg);
505 }
506 _XmStrContState(start) = PUSH_STATE;
507 }
508 return(str);
509 }
510
511 static Boolean
LastSeg(XmStringContext start)512 LastSeg(XmStringContext start)
513 {
514 _XmStringEntry *line;
515
516 if (_XmStrContOpt(start))
517 {
518 return(TRUE);
519 } else {
520 line = _XmStrEntry(_XmStrContString(start));
521
522 if (_XmEntryMultiple(line[_XmStrContCurrLine(start)]))
523 return(_XmStrContCurrSeg(start) == _XmEntrySegmentCount(line));
524 else return(TRUE);
525 }
526 }
527
528 static Boolean
ContextsMatch(XmStringContext a,XmStringContext b)529 ContextsMatch(XmStringContext a,
530 XmStringContext b)
531 {
532 if ((_XmStrContCurrLine(a) == _XmStrContCurrLine(b)) &&
533 (_XmStrContCurrSeg(a) == _XmStrContCurrSeg(b)) &&
534 (_XmStrContState(a) == _XmStrContState(b)))
535
536 if (((_XmStrContState(a) == BEGIN_REND_STATE) ||
537 (_XmStrContState(a) == END_REND_STATE)))
538
539 if (_XmStrContRendIndex(a) == _XmStrContRendIndex(b))
540 return(TRUE);
541 else return(FALSE);
542
543 else return(TRUE);
544
545 else return(FALSE);
546 }
547
548 static XmString
MakeStr(XmStringContext start,XmStringContext end)549 MakeStr(XmStringContext start,
550 XmStringContext end)
551 {
552 /* This is quick and dirty, need to be smarter about it before Beta. */
553 XmStringComponentType type;
554 unsigned int len;
555 XtPointer val;
556 XmString str;
557
558
559 /* Next component over start until at segment break */
560 str = NULL;
561
562 while (_XmStrContState(start) != PUSH_STATE)
563 {
564 type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
565
566 if (ContextsMatch(start, end)) return(str);
567
568 str = XmStringConcatAndFree(str,
569 XmStringComponentCreate(type, len, val));
570 }
571
572 /* Next segment over start incrementing until one segment before context */
573 while ((_XmStrContCurrLine(start) < (_XmStrContCurrLine(end) - 1)) ||
574 ((_XmStrContCurrLine(start) == _XmStrContCurrLine(end)) &&
575 (_XmStrContCurrSeg(start) < _XmStrContCurrSeg(end))) ||
576 !LastSeg(start))
577 {
578 str = XmStringConcatAndFree(str, MakeStrFromSeg(start));
579 }
580
581 /* Next component over start until it matches context */
582 type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
583 while (!ContextsMatch(start, end))
584 {
585 str = XmStringConcatAndFree(str, XmStringComponentCreate(type, len, val));
586 type = XmeStringGetComponent(start, TRUE, FALSE, &len, &val);
587 }
588
589 return(str);
590 }
591
592 Cardinal
XmStringToXmStringTable(XmString string,XmString break_component,XmStringTable * table)593 XmStringToXmStringTable(XmString string,
594 XmString break_component,
595 XmStringTable *table)
596 {
597 /* Note: this is a very expensive way to do this. Fix for Beta */
598 _XmStringContextRec stack_context, stack_start;
599 XmStringComponentType type, b_type;
600 unsigned int len, b_len;
601 XtPointer val, b_val;
602 int i, count;
603
604 _XmProcessLock();
605 /* Get triple for first component of break_component */
606 if (break_component)
607 {
608 _XmStringContextReInit(&stack_context, break_component);
609 b_type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
610 &b_len, &b_val);
611 _XmStringContextFree(&stack_context);
612 }
613 else
614 /* Nothing to match against. Return complete string. */
615 {
616 if (table != NULL)
617 {
618 *table = (XmStringTable)XtMalloc(sizeof(XmString));
619 *table[0] = XmStringCopy(string);
620 }
621 _XmProcessUnlock();
622 return(1);
623 }
624
625 /* Get context */
626 if (!string)
627 {
628 if (table != NULL) *table = NULL;
629 _XmProcessUnlock();
630 return(0);
631 }
632 _XmStringContextReInit(&stack_context, string);
633
634 /* Count number of entries for table */
635 count = 0;
636 while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
637 &len, &val)) !=
638 XmSTRING_COMPONENT_END)
639 {
640 if ((type == b_type) && (len == b_len) &&
641 (memcmp(val, b_val, len) == 0))
642 count++;
643 }
644
645 /* Allocate table and insert new strings */
646 if (table != NULL)
647 {
648 *table = (XmStringTable)XtMalloc(count * sizeof(XmString));
649
650 _XmStringContextReInit(&stack_context, string);
651 _XmStringContextReInit(&stack_start, string);
652
653 i = 0;
654
655 while ((type = XmeStringGetComponent(&stack_context, TRUE, FALSE,
656 &len, &val)) !=
657 XmSTRING_COMPONENT_END)
658 {
659 if ((type == b_type) && (len == b_len) &&
660 (memcmp(val, b_val, len) == 0))
661 {
662 /* make XmString from start to end */
663 (*table)[i] = MakeStr(&stack_start, &stack_context);
664 i++;
665 }
666 }
667
668 _XmStringContextFree(&stack_start);
669 }
670 _XmStringContextFree(&stack_context);
671
672 _XmProcessUnlock();
673 return(count);
674 }
675
676
677
678 XmTabList
XmStringTableProposeTablist(XmStringTable strings,Cardinal num_strings,Widget widget,float pad_value,XmOffsetModel offset_model)679 XmStringTableProposeTablist(XmStringTable strings,
680 Cardinal num_strings,
681 Widget widget,
682 float pad_value,
683 XmOffsetModel offset_model)
684 {
685 int i, j;
686 _XmStringContextRec stack_ctx;
687 XmTabList tl;
688 XmTab tab, prev, start;
689 float width, val;
690 unsigned char units;
691 Arg args[1];
692 int n;
693 _XmRenditionRec scratch;
694 XmRendition rend;
695 _XmRendition tmp;
696 XmRenderTable rt;
697 NextTabResult ret_val;
698
699 _XmProcessLock();
700 if ((strings == NULL) || (num_strings == 0)) {
701 _XmProcessUnlock();
702 return ((XmTabList)NULL);
703 }
704
705 bzero((char*) &scratch, sizeof(_XmRenditionRec));
706 tmp = &scratch;
707 rend = &tmp;
708
709 _XmRendDisplay(rend) = XtDisplayOfObject(widget);
710
711 n = 0;
712 XtSetArg(args[n], XmNrenderTable, &rt); n++;
713 XtGetValues(widget, args, n);
714
715 /* Work around weird bug with XtGetValues. */
716 n = 0;
717 XtSetArg(args[n], XmNunitType, &units); n++;
718 XtGetValues(widget, args, n);
719
720 if (rt == NULL) rt = XmeGetDefaultRenderTable(widget, XmTEXT_FONTLIST);
721
722 tab = XmTabCreate(0.0, units, offset_model, XmALIGNMENT_BEGINNING, ".");
723
724 tl = XmTabListInsertTabs(NULL, &tab, 1, 0);
725
726 XmTabFree(tab);
727
728 for (i = 0; i < num_strings; i++)
729 {
730 if (!strings[i])
731 {
732 /* Clean up */
733 XmTabListFree(tl);
734 _XmProcessUnlock();
735 return((XmTabList)NULL);
736 }
737 _XmStringContextReInit(&stack_ctx, strings[i]);
738
739 tab = _XmTabLStart(tl);
740 val = 0.0;
741
742 /* Scan str for tabs, update tl if necessary. */
743 j = 0;
744
745 while ((ret_val = _XmStringGetNextTabWidth(&stack_ctx, widget, units,
746 rt, &width, &rend)) !=
747 XmTAB_EOS)
748 {
749 if (ret_val == XmTAB_NEWLINE)
750 {
751 tab = _XmTabLStart(tl);
752 j = 0;
753 continue;
754 }
755
756 val = width + pad_value;
757
758 if (j >= _XmTabLCount(tl))
759 /* Need to add a tab */
760 {
761 tab = XmTabCreate(0.0, units, offset_model,
762 XmALIGNMENT_BEGINNING, ".");
763 start = _XmTabLStart(tl);
764 prev = _XmTabPrev(start);
765
766 _XmTabNext(prev) = tab;
767 _XmTabPrev(tab) = prev;
768 _XmTabNext(tab) = start;
769 _XmTabPrev(start) = tab;
770 _XmTabLCount(tl)++;
771 }
772 else if (j > 0)
773 {
774 tab = _XmTabNext(tab);
775 }
776
777 if (val > _XmTabValue(tab)) XmTabSetValue(tab, val);
778 else val = _XmTabValue(tab);
779 j++;
780 }
781
782 _XmStringContextFree(&stack_ctx);
783 }
784
785 if (offset_model == XmABSOLUTE)
786 {
787 start = _XmTabLStart(tl);
788 val = _XmTabValue(start);
789
790 for (tab = _XmTabNext(start); tab != start; tab = _XmTabNext(tab))
791 {
792 val += _XmTabValue(tab);
793 XmTabSetValue(tab, val);
794 }
795 }
796
797 _XmProcessUnlock();
798 return(tl);
799 }
800
801 /*
802 * Helper function for XmTabList.c
803 * This routine performs successive reads on an XmStringContext
804 * and returns the width (in units of XmNunitType of widget) of
805 * the text segments between the previous and next tab or end of line.
806 * It uses the XmNrenderTable from widget to calculate the width. It
807 * returns XmTAB_EOS if the end of the string has been reached, XmTAB_NEWLINE
808 * if the end of line is reached and XmTAB_NEXT if a tab is encountered.
809 */
810 NextTabResult
_XmStringGetNextTabWidth(XmStringContext ctx,Widget widget,unsigned char units,XmRenderTable rt,float * width,XmRendition * rend)811 _XmStringGetNextTabWidth(XmStringContext ctx,
812 Widget widget,
813 unsigned char units,
814 XmRenderTable rt,
815 float *width,
816 XmRendition *rend)
817 {
818 float divisor;
819 int toType; /* passed to XmConvertUnits */
820 Dimension w_sum, w_cur;
821
822 if (_XmStrContError(ctx))
823 {
824 *width = 0.0;
825 return(XmTAB_EOS);
826 }
827
828 w_sum = 0;
829 *width = 0.0;
830
831 /* Big units need to be converted to small ones. */
832 toType = _XmConvertFactor(units, &divisor);
833
834 /* Calculate the width to the next tab. */
835 if (_XmStrContOpt(ctx))
836 {
837 _XmStrContError(ctx) = True;
838 return(XmTAB_EOS);
839 }
840 else
841 {
842 _XmString str = _XmStrContString(ctx);
843 _XmStringEntry line;
844 int line_count;
845 _XmStringEntry seg;
846 int seg_count;
847 _XmStringArraySegRec array_seg;
848
849 line_count = _XmStrLineCountGet(str);
850
851 /* Keep checking lines and segments until we run out or hit a tab. */
852 if (_XmStrContCurrLine(ctx) < line_count)
853 {
854 if (_XmStrImplicitLine(str))
855 {
856 line = _XmStrEntry(str)[_XmStrContCurrLine(ctx)];
857 }
858 else
859 {
860 _XmEntryType(&array_seg) = XmSTRING_ENTRY_ARRAY;
861 _XmEntrySegmentCount(&array_seg) = _XmStrEntryCount(str);
862 _XmEntrySegment(&array_seg) = (_XmStringNREntry *)_XmStrEntry(str);
863 line = (_XmStringEntry)&array_seg;
864 }
865
866 if (_XmEntryMultiple(line))
867 seg_count = _XmEntrySegmentCount(line);
868 else
869 seg_count = 1;
870
871 if (seg_count == 0) {
872 /* Empty line. */
873 _XmStrContCurrLine(ctx)++;
874 *width = 0.0;
875 return(XmTAB_NEWLINE);
876 }
877
878 while (_XmStrContCurrSeg(ctx) < seg_count)
879 {
880 if (_XmEntryMultiple(line))
881 seg = (_XmStringEntry)_XmEntrySegment(line)[_XmStrContCurrSeg(ctx)];
882 else
883 seg = line;
884
885 w_cur = 0;
886
887 if (_XmStrContTabCount(ctx) < _XmEntryTabsGet(seg)) {
888 _XmStrContTabCount(ctx)++;
889 *width = (XmConvertUnits(widget, XmHORIZONTAL,
890 XmPIXELS, w_sum, toType) / divisor);
891 return(XmTAB_NEXT);
892 }
893
894 (void)_XmStringSegmentExtents(seg, rt, rend, NULL,
895 &w_cur, NULL, NULL, NULL);
896 w_sum += w_cur;
897
898 _XmStrContCurrSeg(ctx)++;
899 _XmStrContTabCount(ctx) = 0;
900 }
901
902 _XmStrContCurrLine(ctx)++;
903 _XmStrContCurrSeg(ctx) = 0;
904 _XmStrContTabCount(ctx) = 0;
905
906 return(XmTAB_NEWLINE);
907 }
908
909 _XmStrContError(ctx) = True;
910 return(XmTAB_EOS);
911 }
912 }
913
914