chiark / gitweb /
Imported Upstream version 1.0.0
[e16] / src / text.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "eimage.h"
26 #include "tclass.h"
27 #include "xwin.h"
28
29 static              GC
30 _get_gc(Win win)
31 {
32    static GC           gc = None;
33    static Visual      *last_vis = NULL;
34    Visual             *vis;
35
36    vis = WinGetVisual(win);
37    if (vis != last_vis)
38      {
39         if (gc)
40            EXFreeGC(gc);
41         gc = None;
42         last_vis = vis;
43      }
44
45    if (!gc)
46       gc = EXCreateGC(WinGetXwin(win), 0, NULL);
47
48    return gc;
49 }
50
51 static void
52 TextDrawRotTo(Win win, Drawable src, Drawable dst, int x, int y,
53               int w, int h, TextState * ts)
54 {
55    EImage             *im;
56    int                 win_w;
57
58    switch (ts->style.orientation)
59      {
60      case FONT_TO_UP:
61         im = EImageGrabDrawable(src, 0, y, x, h, w, 0);
62         EImageOrientate(im, 1);
63         EImageRenderOnDrawable(im, win, dst, 0, 0, 0, w, h);
64         EImageFree(im);
65         break;
66      case FONT_TO_DOWN:
67         EXGetGeometry(src, NULL, NULL, NULL, &win_w, NULL, NULL, NULL);
68         im = EImageGrabDrawable(src, None, win_w - y - h, x, h, w, 0);
69         EImageOrientate(im, 3);
70         EImageRenderOnDrawable(im, win, dst, 0, 0, 0, w, h);
71         EImageFree(im);
72         break;
73      case FONT_TO_LEFT: /* Holy carumba! That's for yoga addicts, maybe .... */
74         im = EImageGrabDrawable(src, None, x, y, w, h, 0);
75         EImageOrientate(im, 2);
76         EImageRenderOnDrawable(im, win, dst, 0, 0, 0, w, h);
77         EImageFree(im);
78         break;
79      default:
80         break;
81      }
82 }
83
84 static void
85 TextDrawRotBack(Win win, Drawable dst, Drawable src, int x, int y,
86                 int w, int h, TextState * ts)
87 {
88    EImage             *im;
89    int                 win_w;
90
91    switch (ts->style.orientation)
92      {
93      case FONT_TO_UP:
94         im = EImageGrabDrawable(src, None, 0, 0, w, h, 0);
95         EImageOrientate(im, 3);
96         EImageRenderOnDrawable(im, win, dst, 0, y, x, h, w);
97         EImageFree(im);
98         break;
99      case FONT_TO_DOWN:
100         EXGetGeometry(dst, NULL, NULL, NULL, &win_w, NULL, NULL, NULL);
101         im = EImageGrabDrawable(src, None, 0, 0, w, h, 0);
102         EImageOrientate(im, 1);
103         EImageRenderOnDrawable(im, win, dst, 0, win_w - y - h, x, h, w);
104         EImageFree(im);
105         break;
106      case FONT_TO_LEFT: /* Holy carumba! That's for yoga addicts, maybe .... */
107         im = EImageGrabDrawable(src, None, 0, 0, w, h, 0);
108         EImageOrientate(im, 2);
109         EImageRenderOnDrawable(im, win, dst, 0, x, y, w, h);
110         EImageFree(im);
111         break;
112      default:
113         break;
114      }
115 }
116
117 #if FONT_TYPE_IFT
118 static EImage      *
119 TextImageGet(Win win __UNUSED__, Drawable src, int x, int y, int w, int h,
120              TextState * ts)
121 {
122    EImage             *im;
123    int                 win_w;
124
125    switch (ts->style.orientation)
126      {
127      default:
128      case FONT_TO_RIGHT:
129         im = EImageGrabDrawable(src, None, x, y, w, h, 0);
130         break;
131      case FONT_TO_LEFT:
132         im = EImageGrabDrawable(src, None, x, y, w, h, 0);
133         EImageOrientate(im, 2);
134         break;
135      case FONT_TO_UP:
136         im = EImageGrabDrawable(src, 0, y, x, h, w, 0);
137         EImageOrientate(im, 1);
138         break;
139      case FONT_TO_DOWN:
140         EXGetGeometry(src, NULL, NULL, NULL, &win_w, NULL, NULL, NULL);
141         im = EImageGrabDrawable(src, None, win_w - y - h, x, h, w, 0);
142         EImageOrientate(im, 3);
143         break;
144      }
145
146    return im;
147 }
148
149 static void
150 TextImagePut(EImage * im, Win win, Drawable dst, int x, int y,
151              int w, int h, TextState * ts)
152 {
153    int                 win_w;
154
155    switch (ts->style.orientation)
156      {
157      default:
158      case FONT_TO_RIGHT:
159         EImageRenderOnDrawable(im, win, dst, 0, x, y, w, h);
160         break;
161      case FONT_TO_LEFT:
162         EImageOrientate(im, 2);
163         EImageRenderOnDrawable(im, win, dst, 0, x, y, w, h);
164         break;
165      case FONT_TO_UP:
166         EImageOrientate(im, 3);
167         EImageRenderOnDrawable(im, win, dst, 0, y, x, h, w);
168         break;
169      case FONT_TO_DOWN:
170         EXGetGeometry(dst, NULL, NULL, NULL, &win_w, NULL, NULL, NULL);
171         EImageOrientate(im, 1);
172         EImageRenderOnDrawable(im, win, dst, 0, win_w - y - h, x, h, w);
173         break;
174      }
175    EImageFree(im);
176 }
177 #endif /* FONT_TYPE_IFT */
178
179 TextState          *
180 TextclassGetTextState(TextClass * tclass, int state, int active, int sticky)
181 {
182    if (!tclass)
183       return NULL;
184
185    if (active)
186      {
187         if (!sticky)
188           {
189              switch (state)
190                {
191                case STATE_NORMAL:
192                   return tclass->active.normal;
193                case STATE_HILITED:
194                   return tclass->active.hilited;
195                case STATE_CLICKED:
196                   return tclass->active.clicked;
197                case STATE_DISABLED:
198                   return tclass->active.disabled;
199                default:
200                   break;
201                }
202           }
203         else
204           {
205              switch (state)
206                {
207                case STATE_NORMAL:
208                   return tclass->sticky_active.normal;
209                case STATE_HILITED:
210                   return tclass->sticky_active.hilited;
211                case STATE_CLICKED:
212                   return tclass->sticky_active.clicked;
213                case STATE_DISABLED:
214                   return tclass->sticky_active.disabled;
215                default:
216                   break;
217                }
218
219           }
220      }
221    else if (sticky)
222      {
223         switch (state)
224           {
225           case STATE_NORMAL:
226              return tclass->sticky.normal;
227           case STATE_HILITED:
228              return tclass->sticky.hilited;
229           case STATE_CLICKED:
230              return tclass->sticky.clicked;
231           case STATE_DISABLED:
232              return tclass->sticky.disabled;
233           default:
234              break;
235           }
236      }
237    else
238      {
239         switch (state)
240           {
241           case STATE_NORMAL:
242              return tclass->norm.normal;
243           case STATE_HILITED:
244              return tclass->norm.hilited;
245           case STATE_CLICKED:
246              return tclass->norm.clicked;
247           case STATE_DISABLED:
248              return tclass->norm.disabled;
249           default:
250              break;
251           }
252      }
253    return NULL;
254 }
255
256 static void
257 TextstateTextFit1(TextState * ts, char **ptext, int *pw, int textwidth_limit)
258 {
259    char               *text = *ptext;
260    int                 hh, ascent;
261    char               *new_line;
262    int                 nuke_count = 0, nc2;
263    int                 len;
264
265    len = strlen(text);
266    if (len <= 1)
267       return;
268    new_line = EMALLOC(char, len + 10);
269    if (!new_line)
270       return;
271
272    while (*pw > textwidth_limit)
273      {
274         nuke_count++;
275
276         if (nuke_count >= len - 1)
277           {
278              new_line[0] = text[0];
279              memcpy(new_line + 1, "...", 4);
280              break;
281           }
282
283         nc2 = (len - nuke_count) / 2;
284         memcpy(new_line, text, nc2);
285         memcpy(new_line + nc2, "...", 3);
286         strcpy(new_line + nc2 + 3, text + nc2 + nuke_count);
287
288         ts->ops->TextSize(ts, new_line, 0, pw, &hh, &ascent);
289      }
290
291    Efree(text);
292    *ptext = new_line;
293 }
294
295 #if FONT_TYPE_XFONT
296 static void
297 TextstateTextFit2(TextState * ts, char **ptext, int *pw, int textwidth_limit)
298 {
299    char               *text = *ptext;
300    int                 hh, ascent;
301    char               *new_line;
302    int                 nuke_count = 0;
303    int                 len;
304
305    len = strlen(text);
306    new_line = EMALLOC(char, len + 20);
307
308    if (!new_line)
309       return;
310
311    while (*pw > textwidth_limit)
312      {
313         nuke_count += 2;
314
315         if (nuke_count > len)
316           {
317              memcpy(new_line, text, 2);
318              memcpy(new_line + 2, ". . . ", 7);
319              break;
320           }
321
322         new_line[0] = 0;
323         strncat(new_line, text, (len - nuke_count) / 4);
324         strcat(new_line, ". . . ");
325         strcat(new_line, text + ((len - nuke_count) / 4) + nuke_count);
326
327         ts->ops->TextSize(ts, new_line, 0, pw, &hh, &ascent);
328      }
329
330    Efree(text);
331    *ptext = new_line;
332 }
333 #endif /* FONT_TYPE_XFONT */
334
335 static void
336 TextstateTextFitMB(TextState * ts, char **ptext, int *pw, int textwidth_limit)
337 {
338    char               *text = *ptext;
339    int                 hh, ascent;
340    char               *new_line;
341    int                 nuke_count = 0, nc2;
342    int                 len;
343    wchar_t            *wc_line = NULL;
344    int                 wc_len;
345
346    if (EwcOpen(ts->need_utf8 || Mode.locale.utf8_int))
347       return;
348
349    len = strlen(text);
350    wc_len = EwcStrToWcs(text, len, NULL, 0);
351    if (wc_len <= 1)
352       goto done;
353
354    wc_line = EMALLOC(wchar_t, wc_len + 1);
355    if (!wc_line)
356       goto done;
357
358    if (EwcStrToWcs(text, len, wc_line, wc_len) <= 0)
359       goto done;
360
361    new_line = EMALLOC(char, len + 10);
362
363    if (!new_line)
364       goto done;
365
366    while (*pw > textwidth_limit)
367      {
368         int                 len_mb;
369
370         nuke_count++;
371         if (nuke_count >= wc_len - 1)
372           {
373              int                 mlen;
374
375              mlen = EwcWcsToStr(wc_line, 1, new_line, MB_CUR_MAX);
376              if (mlen < 0)
377                 mlen = 1;
378
379              strcpy(new_line + mlen, "...");
380              break;
381           }
382
383         nc2 = (wc_len - nuke_count) / 2;
384         len_mb = EwcWcsToStr(wc_line, nc2, new_line, len + 10);
385         memcpy(new_line + len_mb, "...", 3);
386         len_mb += 3;
387         len_mb += EwcWcsToStr(wc_line + nc2 + nuke_count,
388                               wc_len - nc2 - nuke_count,
389                               new_line + len_mb, len + 10 - len_mb);
390         new_line[len_mb] = '\0';
391
392         ts->ops->TextSize(ts, new_line, 0, pw, &hh, &ascent);
393      }
394    Efree(text);
395    *ptext = new_line;
396  done:
397    Efree(wc_line);
398    EwcClose();
399 }
400
401 #if FONT_TYPE_XFS
402 /*
403  * XFontSet - XCreateFontSet
404  */
405 extern const FontOps FontOpsXfs;
406
407 typedef struct {
408    XFontSet            font;
409    int                 ascent;
410    Win                 win;
411    Drawable            draw;
412    GC                  gc;
413 } FontCtxXfs;
414
415 static int
416 _xfs_Load(TextState * ts, const char *name)
417 {
418    XFontSet            font;
419    FontCtxXfs         *fdc;
420    int                 i, missing_cnt, font_cnt;
421    char              **missing_list, *def_str, **fnlr;
422    XFontStruct       **fs;
423
424    font = XCreateFontSet(disp, name, &missing_list, &missing_cnt, &def_str);
425    if (missing_cnt)
426       XFreeStringList(missing_list);
427    if (!font)
428       return -1;
429
430    if (EDebug(EDBUG_TYPE_FONTS) >= 2)
431      {
432         Eprintf("- XBaseFontNameListOfFontSet %s\n",
433                 XBaseFontNameListOfFontSet(font));
434         font_cnt = XFontsOfFontSet(font, &fs, &fnlr);
435         for (i = 0; i < font_cnt; i++)
436            Eprintf("- XFontsOfFontSet %d: %s\n", i, fnlr[i]);
437      }
438
439    fdc = EMALLOC(FontCtxXfs, 1);
440    if (!fdc)
441       return -1;
442    fdc->font = font;
443    ts->fdc = fdc;
444    fdc->ascent = 0;
445    font_cnt = XFontsOfFontSet(font, &fs, &fnlr);
446    for (i = 0; i < font_cnt; i++)
447       fdc->ascent = MAX(fs[i]->ascent, fdc->ascent);
448    ts->type = FONT_TYPE_XFS;
449    ts->ops = &FontOpsXfs;
450    return 0;
451 }
452
453 static void
454 _xfs_Unload(TextState * ts)
455 {
456    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
457
458    XFreeFontSet(disp, fdc->font);
459 }
460
461 static void
462 _xfs_TextSize(TextState * ts, const char *text, int len,
463               int *width, int *height, int *ascent)
464 {
465    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
466    XRectangle          ret2;
467
468    if (len == 0)
469       len = strlen(text);
470    XmbTextExtents(fdc->font, text, len, NULL, &ret2);
471    *height = ret2.height;
472    *width = ret2.width;
473    *ascent = fdc->ascent;
474 }
475
476 static void
477 _xfs_TextDraw(TextState * ts, int x, int y, const char *text, int len)
478 {
479    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
480
481    XmbDrawString(disp, fdc->draw, fdc->font, fdc->gc, x, y, text, len);
482 }
483
484 static int
485 _xfs_FdcInit(TextState * ts, Win win, Drawable draw)
486 {
487    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
488
489    fdc->win = win;
490    fdc->draw = draw;
491    fdc->gc = _get_gc(win);
492    return 0;
493 }
494
495 static void
496 _xfs_FdcSetDrawable(TextState * ts, unsigned long draw)
497 {
498    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
499
500    fdc->draw = draw;
501 }
502
503 static void
504 _xfs_FdcSetColor(TextState * ts, EColor * xc)
505 {
506    FontCtxXfs         *fdc = (FontCtxXfs *) ts->fdc;
507
508    EAllocColor(WinGetCmap(fdc->win), xc);
509    XSetForeground(disp, fdc->gc, xc->pixel);
510 }
511
512 const FontOps       FontOpsXfs = {
513    _xfs_Load, _xfs_Unload, _xfs_TextSize, TextstateTextFit, _xfs_TextDraw,
514    _xfs_FdcInit, NULL, _xfs_FdcSetDrawable, _xfs_FdcSetColor
515 };
516 #endif /* FONT_TYPE_XFS */
517
518 #if FONT_TYPE_XFONT
519 /*
520  * XFontStruct - XLoadQueryFont
521  */
522 extern const FontOps FontOpsXfont;
523
524 typedef struct {
525    XFontStruct        *font;
526    Win                 win;
527    Drawable            draw;
528    GC                  gc;
529 } FontCtxXfont;
530
531 static int
532 _xfont_Load(TextState * ts, const char *name)
533 {
534    XFontStruct        *font;
535    FontCtxXfont       *fdc;
536
537    font = XLoadQueryFont(disp, name);
538    if (!font)
539       return -1;
540
541    fdc = EMALLOC(FontCtxXfont, 1);
542    if (!fdc)
543       return -1;
544    fdc->font = font;
545    ts->fdc = fdc;
546    ts->type = FONT_TYPE_XFONT;
547    ts->ops = &FontOpsXfont;
548    return 0;
549 }
550
551 static void
552 _xfont_Unload(TextState * ts __UNUSED__)
553 {
554    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
555
556    XFreeFont(disp, fdc->font);
557 }
558
559 static void
560 _xfont_TextSize(TextState * ts, const char *text, int len,
561                 int *width, int *height, int *ascent)
562 {
563    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
564
565    if (len == 0)
566       len = strlen(text);
567    if (fdc->font->min_byte1 == 0 && fdc->font->max_byte1 == 0)
568       *width = XTextWidth(fdc->font, text, len);
569    else
570       *width = XTextWidth16(fdc->font, (XChar2b *) text, len / 2);
571    *height = fdc->font->ascent + fdc->font->descent;
572    *ascent = fdc->font->ascent;
573 }
574
575 static void
576 _xfont_TextDraw(TextState * ts, int x, int y, const char *text, int len)
577 {
578    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
579
580    if (fdc->font->min_byte1 == 0 && fdc->font->max_byte1 == 0)
581       XDrawString(disp, fdc->draw, fdc->gc, x, y, text, len);
582    else
583       XDrawString16(disp, fdc->draw, fdc->gc, x, y, (XChar2b *) text, len);
584 }
585
586 static int
587 _xfont_FdcInit(TextState * ts, Win win, Drawable draw)
588 {
589    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
590
591    fdc->win = win;
592    fdc->draw = draw;
593    fdc->gc = _get_gc(win);
594
595    XSetFont(disp, fdc->gc, fdc->font->fid);
596    return 0;
597 }
598
599 static void
600 _xfont_FdcSetDrawable(TextState * ts, unsigned long draw)
601 {
602    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
603
604    fdc->draw = draw;
605 }
606
607 static void
608 _xfont_FdcSetColor(TextState * ts, EColor * xc)
609 {
610    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
611
612    EAllocColor(WinGetCmap(fdc->win), xc);
613    XSetForeground(disp, fdc->gc, xc->pixel);
614 }
615
616 static void
617 _xfont_TextFit(TextState * ts, char **ptext, int *pw, int textwidth_limit)
618 {
619    FontCtxXfont       *fdc = (FontCtxXfont *) ts->fdc;
620
621    if (fdc->font->min_byte1 == 0 && fdc->font->max_byte1 == 0)
622       TextstateTextFit1(ts, ptext, pw, textwidth_limit);
623    else
624       TextstateTextFit2(ts, ptext, pw, textwidth_limit);
625 }
626
627 const FontOps       FontOpsXfont = {
628    _xfont_Load, _xfont_Unload, _xfont_TextSize, _xfont_TextFit, _xfont_TextDraw,
629    _xfont_FdcInit, NULL, _xfont_FdcSetDrawable, _xfont_FdcSetColor
630 };
631 #endif /* FONT_TYPE_XFONT */
632
633 static void
634 TsTextDraw(TextState * ts, int x, int y, const char *text, int len)
635 {
636    if (ts->style.effect == 1)
637      {
638         ts->ops->FdcSetColor(ts, &(ts->bg_col));
639         ts->ops->TextDraw(ts, x + 1, y + 1, text, len);
640      }
641    else if (ts->style.effect == 2)
642      {
643         ts->ops->FdcSetColor(ts, &(ts->bg_col));
644         ts->ops->TextDraw(ts, x - 1, y, text, len);
645         ts->ops->TextDraw(ts, x + 1, y, text, len);
646         ts->ops->TextDraw(ts, x, y - 1, text, len);
647         ts->ops->TextDraw(ts, x, y + 1, text, len);
648      }
649    ts->ops->FdcSetColor(ts, &(ts->fg_col));
650    ts->ops->TextDraw(ts, x, y, text, len);
651 }
652
653 typedef struct {
654    const char         *type;
655    const FontOps      *ops;
656    char                checked;
657 } FontHandler;
658
659 #if USE_MODULES
660
661 #define FONT(type, ops, opsm) { type, opsm, 0 }
662
663 #else
664
665 #define FONT(type, ops, opsm) { type, ops, 0 }
666
667 #if FONT_TYPE_IFT
668 extern const FontOps FontOps_ift;
669 #endif
670 #if FONT_TYPE_XFT
671 extern const FontOps FontOps_xft;
672 #endif
673 #if FONT_TYPE_PANGO_XFT
674 extern const FontOps FontOps_pango;
675 #endif
676
677 #endif /* USE_MODULES */
678
679 static FontHandler  fhs[] = {
680 #if FONT_TYPE_XFONT
681    FONT("xfont", &FontOpsXfont, &FontOpsXfont), /* XFontStruct - XLoadQueryFont */
682 #endif
683 #if FONT_TYPE_XFS
684    FONT("xfs", &FontOpsXfs, &FontOpsXfs),       /* XFontSet - XCreateFontSet */
685 #endif
686 #if FONT_TYPE_IFT
687    FONT("ift", &FontOps_ift, NULL),     /* Imlib2/FreeType */
688 #endif
689 #if FONT_TYPE_XFT
690    FONT("xft", &FontOps_xft, NULL),     /* Xft */
691 #endif
692 #if FONT_TYPE_PANGO_XFT
693    FONT("pango", &FontOps_pango, NULL), /* Pango-Xft */
694 #endif
695    {NULL, NULL, 0},
696 };
697
698 static void
699 TextStateLoadFont(TextState * ts)
700 {
701    const char         *s, *type, *name;
702    char                buf[1024];
703    FontHandler        *fhp = fhs;
704
705    if (!ts->fontname)
706       return;
707
708    /* Quit if already done */
709    if (ts->type)
710       return;
711
712    ts->need_utf8 = Mode.locale.utf8_int;
713
714    type = NULL;
715    name = ts->fontname;
716 #if FONT_TYPE_IFT
717    if (strchr(ts->fontname, '/'))
718      {
719         type = "ift";
720         goto check;
721      }
722 #endif
723 #if FONT_TYPE_XFS
724    if (ts->fontname[0] == '-')
725      {
726         type = "xfs";
727         goto check;
728      }
729 #endif
730    s = strchr(ts->fontname, ':');
731    if (!s || s == ts->fontname)
732       goto fallback;
733    if (s - ts->fontname > 16)
734       goto fallback;
735    memcpy(buf, ts->fontname, s - ts->fontname);
736    buf[s - ts->fontname] = '\0';
737    type = buf;
738    name = s + 1;
739
740  check:
741    for (fhp = fhs; fhp->type; fhp++)
742      {
743         if (strcmp(fhp->type, type))
744            continue;
745 #if USE_MODULES
746         if (!fhp->ops)
747           {
748              if (fhp->checked)
749                 goto fallback;
750              fhp->checked = 1;
751              fhp->ops = ModLoadSym("font", "FontOps", type);
752              if (!fhp->ops)
753                 goto fallback;
754           }
755 #endif
756         if (fhp->ops->Load(ts, name) == 0)
757            goto done;
758      }
759  fallback:
760 #if FONT_TYPE_XFS
761    if (!FontOpsXfs.Load(ts, "fixed"))   /* XFontSet - XCreateFontSet */
762       goto done;
763 #endif
764 #if FONT_TYPE_XFONT
765    if (!FontOpsXfont.Load(ts, "fixed")) /* XFontStruct - XLoadQueryFont */
766       goto done;
767 #endif
768
769  done:
770    if (!ts->ops)
771       Eprintf("*** Unable to load font \"%s\"\n", ts->fontname);
772    else if (EDebug(EDBUG_TYPE_FONTS))
773       Eprintf("TextStateLoadFont %s: type=%d\n", ts->fontname, ts->type);
774    return;
775 }
776
777 void
778 TextSize(TextClass * tclass, int active, int sticky, int state,
779          const char *text, int *width, int *height, int fsize __UNUSED__)
780 {
781    const char         *str;
782    char              **lines;
783    int                 i, num_lines, ww, hh, asc;
784    TextState          *ts;
785
786    *width = 0;
787    *height = 0;
788
789    if (!text)
790       return;
791
792    ts = TextclassGetTextState(tclass, state, active, sticky);
793    if (!ts)
794       return;
795
796    TextStateLoadFont(ts);
797    if (!ts->ops)
798       return;
799
800    /* Do encoding conversion, if necessary */
801    str = EstrInt2Enc(text, ts->need_utf8);
802    lines = StrlistFromString(str, '\n', &num_lines);
803    EstrInt2EncFree(str, ts->need_utf8);
804    if (!lines)
805       return;
806
807    for (i = 0; i < num_lines; i++)
808      {
809         ts->ops->TextSize(ts, lines[i], strlen(lines[i]), &ww, &hh, &asc);
810         *height += hh;
811         if (ww > *width)
812            *width = ww;
813      }
814
815    StrlistFree(lines, num_lines);
816 }
817
818 void
819 TextstateTextFit(TextState * ts, char **ptext, int *pw, int textwidth_limit)
820 {
821    if (ts->need_utf8 || MB_CUR_MAX > 1)
822       TextstateTextFitMB(ts, ptext, pw, textwidth_limit);
823    else
824       TextstateTextFit1(ts, ptext, pw, textwidth_limit);
825 }
826
827 void
828 TextstateTextDraw(TextState * ts, Win win, Drawable draw, const char *text,
829                   int x, int y, int w, int h, const EImageBorder * pad,
830                   int fsize __UNUSED__, int justh, int justv)
831 {
832    const char         *str;
833    char              **lines;
834    int                 i, num_lines;
835    int                 textwidth_limit, textheight_limit, offset_x, offset_y;
836    int                 xx, yy, ww, hh, ascent;
837    Pixmap              drawable;
838
839    if (w <= 0 || h <= 0)
840       return;
841
842    TextStateLoadFont(ts);
843    if (!ts->ops)
844       return;
845
846    /* Do encoding conversion, if necessary */
847    str = EstrInt2Enc(text, ts->need_utf8);
848    lines = StrlistFromString(str, '\n', &num_lines);
849    EstrInt2EncFree(str, ts->need_utf8);
850    if (!lines)
851       return;
852
853    if (draw == None)
854       draw = WinGetXwin(win);
855
856    if (ts->style.orientation == FONT_TO_RIGHT ||
857        ts->style.orientation == FONT_TO_LEFT)
858      {
859         if (pad)
860           {
861              x += pad->left;
862              w -= pad->left + pad->right;
863              y += pad->top;
864              h -= pad->top + pad->bottom;
865           }
866         textwidth_limit = w;
867         textheight_limit = h;
868      }
869    else
870      {
871         if (pad)
872           {
873              x += pad->left;
874              h -= pad->left + pad->right;
875              y += pad->top;
876              w -= pad->top + pad->bottom;
877           }
878         textwidth_limit = h;
879         textheight_limit = w;
880      }
881
882 #if 0
883    Eprintf("TextstateTextDraw %d,%d %dx%d(%dx%d): %s\n", x, y, w, h,
884            textwidth_limit, textheight_limit, text);
885 #endif
886
887    xx = x;
888    yy = y;
889
890    if (ts->ops->FdcInit(ts, win, draw))
891       return;
892
893 #if FONT_TYPE_IFT
894    if (ts->type == FONT_TYPE_IFT)
895      {
896         for (i = 0; i < num_lines; i++)
897           {
898              EImage             *im;
899
900              ts->ops->TextSize(ts, lines[i], 0, &ww, &hh, &ascent);
901              if (ww > textwidth_limit)
902                 ts->ops->TextFit(ts, &lines[i], &ww, textwidth_limit);
903
904              if (justv && num_lines == 1 && textheight_limit > 0)
905                 yy += (textheight_limit - hh) / 2;
906              if (i == 0)
907                 yy += ascent;
908              xx = x + (((textwidth_limit - ww) * justh) >> 10);
909
910              im = TextImageGet(win, draw, xx - 1, yy - 1 - ascent,
911                                ww + 2, hh + 2, ts);
912              if (!im)
913                 break;
914
915              offset_x = 1;
916              offset_y = ascent + 1;
917
918              ts->ops->FdcSetDrawable(ts, (unsigned long)im);
919
920              TsTextDraw(ts, offset_x, offset_y, lines[i], strlen(lines[i]));
921
922              TextImagePut(im, win, draw, xx - 1, yy - 1 - ascent,
923                           ww + 2, hh + 2, ts);
924
925              yy += hh;
926           }
927      }
928    else
929 #endif /* FONT_TYPE_IFT */
930      {
931         for (i = 0; i < num_lines; i++)
932           {
933              ts->ops->TextSize(ts, lines[i], 0, &ww, &hh, &ascent);
934              if (ww > textwidth_limit)
935                 ts->ops->TextFit(ts, &lines[i], &ww, textwidth_limit);
936
937              if (justv && num_lines == 1 && textheight_limit > 0)
938                 yy += (textheight_limit - hh) / 2;
939              if (i == 0)
940                 yy += ascent;
941              xx = x + (((textwidth_limit - ww) * justh) >> 10);
942
943              if (ts->style.orientation != FONT_TO_RIGHT)
944                 drawable = ECreatePixmap(win, ww + 2, hh + 2, 0);
945              else
946                 drawable = draw;
947              TextDrawRotTo(win, draw, drawable, xx - 1, yy - 1 - ascent,
948                            ww + 2, hh + 2, ts);
949
950              if (ts->style.orientation == FONT_TO_RIGHT)
951                {
952                   offset_x = xx;
953                   offset_y = yy;
954                }
955              else
956                {
957                   offset_x = 1;
958                   offset_y = ascent + 1;
959                }
960
961              if (drawable != draw)
962                 ts->ops->FdcSetDrawable(ts, drawable);
963
964              TsTextDraw(ts, offset_x, offset_y, lines[i], strlen(lines[i]));
965
966              TextDrawRotBack(win, draw, drawable, xx - 1, yy - 1 - ascent,
967                              ww + 2, hh + 2, ts);
968              if (drawable != draw)
969                 EFreePixmap(drawable);
970
971              yy += hh;
972           }
973      }
974
975    if (ts->ops->FdcFini)
976       ts->ops->FdcFini(ts);
977
978    StrlistFree(lines, num_lines);
979 }
980
981 void
982 TextDraw(TextClass * tclass, Win win, Drawable draw, int active, int sticky,
983          int state, const char *text, int x, int y, int w, int h, int fsize,
984          int justh)
985 {
986    TextState          *ts;
987
988    if (!tclass || !text)
989       return;
990
991    ts = TextclassGetTextState(tclass, state, active, sticky);
992    if (!ts)
993       return;
994
995    TextstateTextDraw(ts, win, draw, text, x, y, w, h, NULL, fsize, justh, 0);
996 }