1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /**
20  * \file xtext.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao and Mark Overmars
24  *  All rights reserved.
25  *
26  * All text routines. There are rooms for speed ups. For one, font
27  * switching can be reduced somewhat.
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include "include/forms.h"
35 #include "flinternal.h"
36 
37 #include <string.h>
38 #include <ctype.h>
39 
40 
41 static int UL_thickness = -1;
42 static int UL_propwidth = 1;    /* 1 for proportional, 0 for constant */
43 
44 static void do_underline( FL_Coord,
45                           FL_Coord,
46                           const char *,
47                           int );
48 
49 static void do_underline_all( FL_Coord,
50                               FL_Coord,
51                               const char *,
52                               int,
53                               unsigned long *,
54                               unsigned long * );
55 
56 #define NUM_LINES_INCREMENT  64
57 
58 static struct LINE_INFO {
59     char * str;
60     int    len;
61     int    index;
62     int    underline_index;
63     int    x;
64     int    y;
65 } * lines = NULL;
66 
67 static int nlines;
68 
69 static int max_pixelline = 0;
70 
71 
72 /***************************************
73  ***************************************/
74 
75 static int
extend_workmem(int nl)76 extend_workmem( int nl )
77 {
78     lines = fl_realloc( lines, nl * sizeof *lines );
79     return nlines = nl;
80 }
81 
82 
83 /***************************************
84  ***************************************/
85 
86 void
fli_free_xtext_workmem(void)87 fli_free_xtext_workmem( void )
88 {
89     fli_safe_free( lines );
90     nlines = 0;
91 }
92 
93 
94 /***************************************
95  * Returns the index of the widest line drawn in a previous
96  * call of fli_draw_string() (only used by input.c)
97  ***************************************/
98 
99 int
fli_get_max_pixels_line(void)100 fli_get_max_pixels_line( void )
101 {
102     return max_pixelline;
103 }
104 
105 
106 /* type fitting both XDrawString() and XDrawImageString() */
107 
108 typedef int ( * DrawString )( Display    * display,
109                               Drawable     d,
110                               GC           gc,
111                               int          x,
112                               int          y,
113                               const char * string,
114                               int          length );
115 
116 
117 /***************************************
118  * Major text drawing routine. It draws text (possibly consisting of several
119  * lines) into the box specified via the coordinates and using the given
120  * alignment relative to that box. Also a cursor is drawn. For lines that
121  * contain a special character indicating underlining this is also handled.
122  * Finally, parts of the text can be shown as selected.
123  *
124  * Arguments:
125  *  align       Alignment of the text relative to the box
126  *  x, ym w, h  postion and size of box
127  *  clip        0 = no clipping is to be used at all
128  *              1 = clipping is to be done by this function
129  *              -1 = clipping is done but already has been set externally
130  *  backcol     color to be used for selected text
131  *  forecol     color for (unselected) text
132  *  curscol     color for cursor
133  *  style       text font style
134  *  size        text font size
135  *  curspos     index into string where cursor is to be drawn (if negative or
136  *              non-zero and there's no text no cursor is drawn)
137  *  selstart    index into string where selection starts
138  *  selend      index into string where selection ends (to get selection this
139  *              must be larger than 'selstart')
140  *  istr        pointer to the text to be drawn
141  *  img         if non-zero also draw background (use XDrawImageString()
142  *              instead of XDrawString() to draw the text)
143  *  topline     first line of the text to be actually shown (counting starts
144  *              at 1)
145  *  endline     last line to be shown (if less than 1 or too large is replaced
146  *              by the number of lines in the text)
147  *  bkcol       background color (used when 'img' is true)
148  *
149  * Returns the width (in pixel) of the widest line of the text drawn.
150  ***************************************/
151 
152 int
fli_draw_string(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,int clip,FL_COLOR backcol,FL_COLOR forecol,FL_COLOR curscol,int style,int size,int curspos,int selstart,int selend,const char * istr,int img,int topline,int endline,FL_COLOR bkcol)153 fli_draw_string( int           align,
154                  FL_Coord      x,
155                  FL_Coord      y,
156                  FL_Coord      w,
157                  FL_Coord      h,
158                  int           clip,
159                  FL_COLOR      backcol,
160                  FL_COLOR      forecol,
161                  FL_COLOR      curscol,
162                  int           style,
163                  int           size,
164                  int           curspos,
165                  int           selstart,
166                  int           selend,
167                  const char *  istr,
168                  int           img,
169                  int           topline,
170                  int           endline,
171                  FL_COLOR      bkcol )
172 {
173     int i;
174     int lnumb = 0;           /* number of lines in string */
175     int max_pixels = 0;
176     int horalign,
177         vertalign;
178     char * str = NULL,
179          * p = NULL;
180     DrawString drawIt = img ? XDrawImageString : XDrawString;
181 
182     /* Check if anything has to be drawn at all - do nothing if we either
183        have no window or the cursor is to be drawn somewhere else than in
184        the very first position and there's no string to output. It would
185        be tempting to also bail out if the height 'h' is 0 or even negative
186        but there are some code paths were this actually may happen and we
187        wouldn't output a string even though it is needed (I know, it's a
188        bloody mess but fixing it right now would probably take a few weeks
189        and even might break existing code...) */
190 
191     if (    flx->win == None
192          || ( curspos > 0 && ! ( istr && *istr ) ) )
193         return 0;
194 
195     /* We operate only on a copy of the input string */
196 
197     if ( istr && *istr )
198         p = str = fl_strdup( istr );
199 
200     /* Split the string into lines, store the index where each of them begins
201        in the original string as well as the length */
202 
203     while ( p )
204     {
205         /* Make sure we have enough memory */
206 
207         if ( lnumb >= nlines )
208             extend_workmem( nlines + NUM_LINES_INCREMENT );
209 
210         /* Get pointer to the start of the line and it's index in the
211            complete string */
212 
213         lines[ lnumb ].str = p;
214         lines[ lnumb ].index = p - str;   /* where line begins in str */
215 
216         /* Try to find the next new line and replace the '\n' with '\0' */
217 
218         if ( ( p = strchr( p, '\n' ) ) )
219             *p++ = '\0';
220 
221         /* Calculate the length of the string */
222 
223         lines[ lnumb ].len = p ? ( p - lines[ lnumb ].str - 1 ) :
224                                  ( int ) strlen( lines[ lnumb ].str );
225         ++lnumb;
226     }
227 
228     /* Correct values for the top and end line to be shown (they are given
229        starting at 1) */
230 
231     if ( --topline < 0 || topline >= lnumb )
232         topline = 0;
233 
234     if ( --endline >= lnumb || endline < 0 )
235         endline = lnumb;
236 
237     /* Calculate coordinates of all lines (for y the baseline position),
238      for that make sure the correct font is set up */
239 
240     fl_set_font( style, size );
241     fli_get_hv_align( align, &horalign, &vertalign );
242 
243     for ( i = topline; i < endline; i++ )
244     {
245         struct LINE_INFO *line = lines + i;
246         int width;
247 
248         /* Check for the special character which indicates underlining (all
249            the line if it's in the very first position, otherwise just after
250            the character to underline), remove it from the string but remember
251            were it was and correct the selection positions if necessary (i.e.
252            if they are in the line after the character to be underlined).
253            Same for the cursor position if it's in the line. */
254 
255         if ( ( p = strchr( line->str, *fl_ul_magic_char ) ) )
256         {
257             line->underline_index = p - line->str;
258 
259             if (    selstart < line->index + line->len
260                  && selstart > line->index + line->underline_index )
261                 --selstart;
262             if (    selend < line->index + line->len
263                  && selend > line->index + line->underline_index )
264                 --selend;
265             if (    curspos >= line->index + line->underline_index
266                  && selstart < line->index + line->len )
267                 --curspos;
268 
269             memmove( p, p + 1, line->len-- - line->underline_index );
270         }
271         else
272             line->underline_index = -1;
273 
274         /* Determine the width (in pixel) of the line) */
275 
276         width = XTextWidth( flx->fs, line->str, line->len );
277 
278         if ( width > max_pixels )
279         {
280             max_pixels = width;
281             max_pixelline = i;
282         }
283 
284         /* Calculate the x- and y- positon of where to print the text */
285 
286         switch ( horalign )
287         {
288             case  FL_ALIGN_LEFT :
289                 line->x = x;
290                 break;
291 
292             case FL_ALIGN_CENTER :
293                 line->x = x + 0.5 * ( w - width );
294                 break;
295 
296             case FL_ALIGN_RIGHT :
297                 line->x = x + w - width;
298                 break;
299 
300             default :
301                 M_err( "fli_draw_string", "This is impossible" );
302                 return 0;
303         }
304 
305         switch ( vertalign )
306         {
307             case FL_ALIGN_TOP :
308                 line->y = y + i * flx->fheight + flx->fasc;
309                 break;
310 
311             case FL_ALIGN_CENTER :
312                 line->y =   y + 0.5 * h + ( i - 0.5 * lnumb ) * flx->fheight
313                           + flx->fasc;
314                 break;
315 
316             case FL_ALIGN_BOTTOM :
317                 line->y = y + h - 1 + ( i - lnumb ) * flx->fheight + flx->fasc;
318                 break;
319 
320             default :
321                 M_err( "fli_draw_string", "This is impossible" );
322                 return 0;
323         }
324     }
325 
326     /* Set clipping if we got asked to */
327 
328     if ( clip > 0 )
329         fl_set_text_clipping( x, y, w, h );
330 
331     /* Set foreground and background color for text */
332 
333     fli_textcolor( forecol );
334     fli_bk_textcolor( bkcol );
335 
336     /* Draw all the lines requested */
337 
338     for ( i = topline; i < endline; i++ )
339     {
340         struct LINE_INFO *line = lines + i;
341         FL_COLOR underline_col = forecol;
342         int xsel = 0,       /* start position of selected text */
343             wsel = 0;       /* and its length (in pixel) */
344 
345         /* Skip lines that can't be visile due to clipping */
346 
347         if ( clip != 0 )
348         {
349             if ( line->y + flx->fdesc < y )
350                 continue;
351             if ( line->y - flx->fasc >= y + h )
352                 break;
353         }
354 
355         /* Draw the text */
356 
357         drawIt( flx->display, flx->win, flx->textgc,
358                 line->x, line->y, line->str, line->len );
359 
360         /* Draw selection area if required - for this we need to draw
361            the selection background and then redraw the text in this
362            region (in the clor that was used for the background before).
363            Of course all this only needs to be done if the selection started
364            before or in this line and didn't end before it. */
365 
366         if (    selstart <  selend
367              && selstart <= line->index + line->len
368              && selend   >  line->index )
369         {
370             int start,     /* start index of selected text */
371                 end,       /* end index */
372                 len;       /* its length (in chars) */
373 
374             /* The selection may have started before the line we're just
375                dealing with and may end after it. Find the start and end
376                position in this line */
377 
378             if ( selstart <= line->index )
379                 start = 0;
380             else
381                 start = selstart - line->index;
382 
383             if ( selend >= line->index + line->len )
384                 end = line->len;
385             else
386                 end = selend - line->index;
387 
388             len = end - start;
389 
390             /* Get the start position and width (in pixels) of the selected
391                region (the -1 in the calculation  of wsel is a fudge factor
392                to make it look a bit better) */
393 
394             xsel = line->x + XTextWidth( flx->fs, line->str, start );
395 
396             wsel = XTextWidth( flx->fs, line->str + start, len ) - 1;
397             if ( xsel + wsel > x + w )
398                 wsel = x + w - xsel;
399 
400             /* Draw in the selection color */
401 
402             fl_rectf( xsel, line->y - flx->fasc, wsel,
403                       flx->fheight, forecol );
404 
405             fli_textcolor( backcol );
406             drawIt( flx->display, flx->win, flx->textgc, xsel,
407                     line->y, line->str + start, len );
408             fli_textcolor( forecol );
409 
410             if ( line->underline_index > 0 )
411                 underline_col = backcol;
412         }
413 
414         /* Next do underlining */
415 
416         if ( line->underline_index > 0 )
417         {
418             fl_color( underline_col );
419             do_underline( line->x, line->y, line->str,
420                           line->underline_index - 1 );
421         }
422         else if ( line->underline_index == 0 )
423         {
424             unsigned long offset,
425                           thickness;
426 
427             fl_color( forecol );
428             do_underline_all( line->x, line->y, line->str,
429                               line->len, &offset, &thickness );
430 
431             /* If wsel is larger than 0 some part of the underlined
432                string is selected and then we need to draw underine of
433                the part of the string that is selected in the color used
434                for drawing that part of the string. */
435 
436             if ( wsel > 0 && thickness > 0 )
437             {
438                 fl_color( underline_col );
439                 XFillRectangle( flx->display, flx->win, flx->gc, xsel,
440                                 line->y + offset, wsel, thickness);
441             }
442         }
443 
444         /* Finally, we also may have to draw a cursor */
445 
446         if (    curspos >= line->index
447              && curspos <= line->index + line->len )
448         {
449             int tt = XTextWidth( flx->fs, line->str,
450                                  curspos - line->index );
451 
452             fl_rectf( line->x + tt, line->y - flx->fasc,
453                       2, flx->fheight, curscol );
454         }
455     }
456 
457     /* One possiblity remains: there's no text but the cursor is to be set
458        at the very first position */
459 
460     if ( curspos == 0 && lnumb == 0 && w >= 2 )
461     {
462         int xc,
463             yc;
464 
465         if ( horalign == FL_ALIGN_LEFT )
466             xc = x;
467         else if ( horalign == FL_ALIGN_CENTER )
468             xc = x + 0.5 * w - 1;
469         else
470             xc = x + w - 2;
471 
472         if ( vertalign == FL_ALIGN_BOTTOM )
473             yc = y + h - 1 - flx->fasc;
474         else if ( vertalign == FL_ALIGN_CENTER )
475             yc = y + 0.5 * ( h - flx->fheight );
476         else
477             yc = y;
478 
479         fl_rectf( xc, yc, 2, flx->fheight, curscol );
480     }
481 
482     /* Free our copy of the string */
483 
484     fli_safe_free( str );
485 
486     /* Reset clipping if required */
487 
488     if ( clip > 0 )
489         fl_unset_text_clipping( );
490 
491     return max_pixels;
492 }
493 
494 
495 /***************************************
496  * Function returns the index of the character in the label of the object
497  * the mouse is over or -1 if it's not over the label. Note that the function
498  * has some limitations: it can only be used on labels inside of the object
499  * and the label string may not contain underline characters (and the label
500  * can't be a symbol) - if you try to use it on labels that don't satisfy
501  * these requirements -1 is returned.
502  ***************************************/
503 
504 int
fl_get_label_char_at_mouse(FL_OBJECT * obj)505 fl_get_label_char_at_mouse( FL_OBJECT * obj )
506 {
507     int x,
508         y,
509         xp,
510         yp,
511         pos,
512         outside;
513     unsigned int dummy;
514 
515     if (    ! obj
516          || ! obj->form
517          || ! fl_is_inside_lalign( obj->align )
518          || ! obj->label || ! *obj->label
519          || strchr( obj->label, *fl_ul_magic_char )
520          || ( obj->label[ 0 ] == '@' && obj->label[ 1 ] != '@' ) )
521         return -1;
522 
523     if (    fl_get_form_mouse( obj->form, &x, &y, &dummy ) != obj->form->window
524          || x < obj->x || x >= obj->x + obj->w
525          || y < obj->y || y >= obj->y + obj->h )
526         return -1;
527 
528     x += 2;
529 
530     pos = fli_get_pos_in_string( obj->align, obj->x, obj->y, obj->w, obj->h,
531                                  obj->lstyle, obj->lsize, x, y, obj->label,
532                                  &xp, &yp, &outside ) - 1;
533 
534     if ( outside )
535         return -1;
536 
537     return pos;
538 }
539 
540 
541 /***************************************
542  * Routine returns the index of the character the mouse is on in a string
543  * via the return value and the line number and character position in the
544  * line via 'yp' and 'xp' (note: they count starting at 1, 0 indicates the
545  * mouse is to the left of the start of the line)
546  * The function expects a string that doesn't contain mon-printable characters
547  * (except '\n' for starts a new lines)
548  * This function is supposed to work on text drawn using fli_draw_string()
549  * using the same relevant arguments (alignment, box, font style and size
550  * and string) as passed to this function.
551  *
552  * Arguments:
553  *  align:       alignment of the text in the box
554  *  x, y, w, h:  box the text is to be found in
555  *  style:       font style used when drawing the text
556  *  size:        font size used when drawing the text
557  *  xpos:        x-position of the mouse
558  *  ypos:        y-position of the mouse
559  *  str:         pointer to the text itself
560  *  xp:          pointer for returning the index in the line where the mouse is
561  *               (tarts at 1 with 0 meaning before the start of the string)
562  *  yp:          pointer for returning the line number (starting at 1)
563  *  outside:     set if the mouse wasn't directly within the string
564  ***************************************/
565 
566 int
fli_get_pos_in_string(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,int style,int size,FL_Coord xpos,FL_Coord ypos,const char * str,int * xp,int * yp,int * outside)567 fli_get_pos_in_string( int          align,
568                        FL_Coord     x,
569                        FL_Coord     y,
570                        FL_Coord     w,
571                        FL_Coord     h,
572                        int          style,
573                        int          size,
574                        FL_Coord     xpos,
575                        FL_Coord     ypos,
576                        const char * str,
577                        int        * xp,
578                        int        * yp,
579                        int        * outside )
580 {
581     int lnumb = 0;             /* number of lines  */
582     int horalign,
583         vertalign;
584     struct LINE_INFO * line;
585     int width;                 /* string width of that line... */
586     int xstart;                /* start x-coordinate of this line  */
587     int toppos;                /* y-coord of the top line  */
588     const char *p = str;
589     int xlen;
590     int fheight;
591     int dummy;
592 
593     /* Give the user some slack in hitting the mark - he might try to place
594        the cursor between two characters and accidentally has the mouse a
595        bit too far to the right. */
596 
597     xpos -= 2;
598     *outside = 0;
599 
600     /* Nothing to be done if there's no string */
601 
602     if ( ! str || ! *str )
603         return 0;
604 
605     /* No need to actually set the font (we're not drawing anything), all
606        required is its height */
607 
608     fheight = fl_get_char_height( style, size, &dummy, &dummy );
609 
610     /* Find all the lines starts etc. in the string */
611 
612     while ( p )
613     {
614         if ( lnumb + 1 >= nlines )
615             extend_workmem( nlines + NUM_LINES_INCREMENT );
616 
617         lines[ lnumb ].str = ( char * ) p;
618         lines[ lnumb++ ].index = p - str;
619         if ( ( p = strchr( p, '\n' ) ) )
620             ++p;
621     }
622 
623     /* Find the line in which the mouse is  */
624 
625     fli_get_hv_align( align, &horalign, &vertalign );
626 
627     switch ( vertalign )
628     {
629         case FL_ALIGN_TOP :
630             toppos = y;
631             break;
632 
633         case FL_ALIGN_CENTER :
634             toppos = y + 0.5 * ( h - lnumb * fheight );
635             break;
636 
637         case FL_ALIGN_BOTTOM :
638             toppos = y + h - 1 - fheight;
639             break;
640 
641         default :
642             M_err( "fli_get_pos_in_string", "This is impossible" );
643             return 0;
644     }
645 
646     *yp = ( ypos - toppos ) / fheight;
647 
648     if ( *yp < 0 )
649     {
650         *outside = 1;
651         *yp = 0;
652     }
653     else if ( *yp >= lnumb )
654     {
655         *outside = 1;
656         *yp = lnumb - 1;
657     }
658 
659     line = lines + *yp;
660 
661     if ( *yp == lnumb - 1 )
662         line->len = strlen( line->str );
663     else
664         line->len = lines[ *yp + 1 ].str - line->str - 1;
665 
666     /* Calculate width and start x-coordinate of the line */
667 
668     width = XTextWidth( flx->fs, line->str, line->len );
669 
670     switch ( horalign )
671     {
672         case FL_ALIGN_LEFT :
673             xstart = x;
674             break;
675 
676         case FL_ALIGN_CENTER :
677             xstart = x + 0.5 * ( w - width );
678             break;
679 
680         case FL_ALIGN_RIGHT :
681             xstart = x + w - width;
682             break;
683 
684         default :
685             M_err( "fli_get_pos_in_string", "This is impossible" );
686             return 0;
687     }
688 
689     xpos -= xstart;
690 
691     /* If the mouse is before or behind the string things are simple.... */
692 
693     if ( xpos <= 0 )
694     {
695         *xp = 0;
696         *yp += 1;
697         *outside = 1;
698         return line->index;
699     }
700     else if ( xpos >= width )
701     {
702         *xp = line->len;
703         *yp += 1;
704         *outside = 1;
705         return line->index + line->len;
706     }
707 
708     /* ...otherwise take a guess at the offset in the string where the mouse
709        is, assuming all chars have the same width */
710 
711     *xp = ( double ) ( xpos * line->len ) / width;
712 
713     xlen = XTextWidth( flx->fs, line->str, ++*xp );
714 
715     /* If we don't have hit it directly search to the left or right */
716 
717     if ( xlen > xpos )
718     {
719         do
720         {
721             *xp -= 1;
722             xlen = XTextWidth( flx->fs, line->str, *xp );
723         }
724         while ( *xp > 0 && xlen > xpos );
725         *xp += 1;
726     }
727     else if ( xlen < xpos )
728         do
729         {
730             *xp += 1;
731             xlen = XTextWidth( flx->fs, line->str, *xp );
732         }
733         while ( *xp < lines->len && xlen < xpos );
734 
735     *yp += 1;
736 
737     return line->index + *xp;
738 }
739 
740 
741 /***
742   Miscellaneous text drawing routines
743 ***/
744 
745 /***************************************
746  * Draw text with cursor and, if 'bk' is set, also the background
747  * (but with no highlighting)
748  ***************************************/
749 
750 static void
fli_draw_text_cursor(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * str,int style,int size,FL_COLOR c,FL_COLOR bc,FL_COLOR cc,int bk,int pos)751 fli_draw_text_cursor( int          align,   /* alignment in box */
752                       FL_Coord     x,       /* box geometry */
753                       FL_Coord     y,
754                       FL_Coord     w,
755                       FL_Coord     h,
756                       const char * str,     /* string to draw */
757                       int          style,   /* font style and size */
758                       int          size,
759                       FL_COLOR     c,       /* color for text */
760                       FL_COLOR     bc,      /* background color */
761                       FL_COLOR     cc,      /* color for cursor */
762                       int          bk,      /* draws background when set */
763                       int          pos )    /* index of cursor position */
764 {
765     fli_draw_string( align, x, y, w, h, 0, FL_NOCOLOR, c, cc,
766                      style, size, pos, 0, -1, str, bk, 0, 0, bc );
767 }
768 
769 
770 /***************************************
771  * Draws a (multi-line) text with a cursor (no background, no highlighting)
772  ***************************************/
773 
774 void
fl_draw_text_cursor(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,FL_COLOR c,int style,int size,const char * str,FL_COLOR cc,int pos)775 fl_draw_text_cursor( int          align,   /* alignment in box */
776                      FL_Coord     x,       /* box geometry */
777                      FL_Coord     y,
778                      FL_Coord     w,
779                      FL_Coord     h,
780                      FL_COLOR     c,       /* text color */
781                      int          style,   /* font style and size */
782                      int          size,
783                      const char * str,     /* the text to draw */
784                      FL_COLOR     cc,      /* cursor color */
785                      int          pos )    /* cursor position */
786 {
787     fli_draw_text_cursor( align, x, y, w, h, str, style, size,
788                           c, FL_NOCOLOR, cc, 0, pos );
789 }
790 
791 
792 /***************************************
793  ***************************************/
794 
795 #define D( x, y, c )                                      \
796     fli_draw_text_cursor( align, x, y, w, h, str,         \
797                           style,size, c, bc, 0, bk, -1 )
798 
799 void
fli_draw_text_inside(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * str,int style,int size,FL_COLOR c,FL_COLOR bc,int bk)800 fli_draw_text_inside( int          align,
801                       FL_Coord     x,
802                       FL_Coord     y,
803                       FL_Coord     w,
804                       FL_Coord     h,
805                       const char * str,
806                       int          style,
807                       int          size,
808                       FL_COLOR     c,
809                       FL_COLOR     bc,
810                       int          bk )
811 {
812     int special = 0;
813     int xoff,
814         yoff;
815     int sw = w,
816         sh = h,
817         sx = x,
818         sy = y;
819 
820     if ( ! str || ! *str )
821         return;
822 
823     if ( str[ 0 ] == '@' && str[ 1 ] != '@' )
824     {
825         if ( w < 5 && h < 5 )
826         {
827             sw = sh = 6 + 1.1 * size;
828             sx -= sw / 2;
829             sy -= sh / 2;
830         }
831 
832         if ( fl_draw_symbol( str, sx, sy, sw, sh, c ) )
833             return;
834         else
835             str++;
836     }
837     else if ( str[ 0 ] == '@' && str[ 1 ] == '@' )
838         str++;
839 
840     xoff = 5;
841     yoff = 4;
842 
843     x += xoff;
844     w -= 2 * xoff;
845     y += yoff;
846     h -= 2 * yoff;
847 
848     if ( special_style( style ) )
849     {
850         special = ( style / FL_SHADOW_STYLE ) * FL_SHADOW_STYLE;
851         style %= FL_SHADOW_STYLE;
852     }
853 
854     /* Take care of special effects stuff  */
855 
856     if ( special == FL_SHADOW_STYLE )
857         D( x + 2, y + 2, FL_BOTTOM_BCOL );
858     else if ( special == FL_ENGRAVED_STYLE )
859     {
860         D( x - 1, y,     FL_RIGHT_BCOL );
861         D( x,     y - 1, FL_RIGHT_BCOL );
862         D( x - 1, y - 1, FL_RIGHT_BCOL );
863         D( x + 1, y,     FL_TOP_BCOL );
864         D( x,     y + 1, FL_TOP_BCOL );
865         D( x + 1, y + 1, FL_TOP_BCOL );
866     }
867     else if ( special == FL_EMBOSSED_STYLE )
868     {
869         D( x - 1, y,     FL_TOP_BCOL );
870         D( x,     y - 1, FL_TOP_BCOL );
871         D( x - 1, y - 1, FL_TOP_BCOL );
872         D( x + 1, y,     FL_RIGHT_BCOL );
873         D( x,     y + 1, FL_RIGHT_BCOL );
874         D( x + 1, y + 1, FL_RIGHT_BCOL );
875     }
876 
877     fli_draw_text_cursor( align, x, y, w, h, str, style, size,
878                           c, bc, FL_NOCOLOR, special ? 0 : bk, -1 );
879 }
880 
881 
882 /***************************************
883  * Draws a text inside a box.
884  ***************************************/
885 
886 void
fl_draw_text(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,FL_COLOR c,int style,int size,const char * str)887 fl_draw_text( int          align,
888               FL_Coord     x,
889               FL_Coord     y,
890               FL_Coord     w,
891               FL_Coord     h,
892               FL_COLOR     c,
893               int          style,
894               int          size,
895               const char * str )
896 {
897     fli_draw_text_inside( align, x, y, w, h, str, style, size, c, 0, 0 );
898 }
899 
900 
901 /***************************************
902  ***************************************/
903 
904 void
fl_draw_text_beside(int align,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,FL_COLOR c,int style,int size,const char * str)905 fl_draw_text_beside( int          align,
906                      FL_Coord     x,
907                      FL_Coord     y,
908                      FL_Coord     w,
909                      FL_Coord     h,
910                      FL_COLOR     c,
911                      int          style,
912                      int          size,
913                      const char * str )
914 {
915     int newa,
916         newx,
917         newy,
918         dx = 0,
919         dy = 0;
920 
921     if ( ! str || ! *str || w <= 0 || h <= 0 )
922         return;
923 
924     if ( fl_is_inside_lalign( align ) && ! fl_is_center_lalign( align ) )
925         M_warn( "drw_text_beside", "align request is inside" );
926 
927     if ( align & FL_ALIGN_LEFT )
928     {
929         if ( align & FL_ALIGN_BOTTOM || align & FL_ALIGN_TOP )
930             dx = - 4;
931         else
932             dx = 1;
933     }
934     else if ( align & FL_ALIGN_RIGHT )
935     {
936         if ( align & FL_ALIGN_BOTTOM || align & FL_ALIGN_TOP )
937             dx = 4;
938         else
939             dx = - 1;
940     }
941 
942     if ( align & FL_ALIGN_BOTTOM )
943         dy = - 2;
944     else if ( align & FL_ALIGN_TOP )
945         dy = 2;
946 
947     x += dx;
948     y += dy;
949 
950     fli_get_outside_align( align, x, y, w, h, &newa, &newx, &newy );
951     fl_draw_text( newa, newx, newy, w, h, c, style, size, str );
952 }
953 
954 
955 /***************************************
956  * Do underlined text, single character only
957  * if underline width to be proportional or fixed width
958  ***************************************/
959 
960 void
fli_set_ul_property(int prop,int thickness)961 fli_set_ul_property( int prop,
962                      int thickness )
963 {
964     UL_propwidth = prop;
965     if ( thickness > 0 )
966         UL_thickness = thickness;
967 }
968 
969 
970 #define DESC( c )   ( c == 'g' || c == 'j' || c == 'q' || c == 'y' || c == 'p' )
971 #define NARROW( c ) ( c == 'i' || c == 'j' || c == 'l' || c == 'f' || c == '1' )
972 
973 /***************************************
974  ***************************************/
975 
976 XRectangle *
fli_get_underline_rect(XFontStruct * fs,FL_Coord x,FL_Coord y,const char * cstr,int n)977 fli_get_underline_rect( XFontStruct * fs,
978                         FL_Coord      x,
979                         FL_Coord      y,
980                         const char  * cstr,
981                         int           n )
982 {
983     static XRectangle xr;
984     int ul_width,
985         ul_rwidth,
986         xoff;
987     unsigned long ul_pos,
988                   ul_thickness = 0;
989     char *str = ( char * ) cstr;
990     int ch = *( str + n );
991     int pre;                /* stuff in front of the string, such as ^H */
992 
993     if ( UL_thickness < 0 )
994         XGetFontProperty( flx->fs, XA_UNDERLINE_THICKNESS, &ul_thickness );
995     else
996         ul_thickness = UL_thickness;
997 
998     if ( ul_thickness == 0 || ul_thickness > 100 )
999         ul_thickness = strstr( fli_curfnt, "bold" ) ? 2 : 1;
1000 
1001     if ( ! XGetFontProperty( fs, XA_UNDERLINE_POSITION, &ul_pos ) )
1002         ul_pos = DESC( ch ) ? ( 1 + flx->fdesc ) : 1;
1003 
1004     /* If the character is narrow, use the width of g otherwise use the width
1005        of D. Of course, if UL_width == proportional, this really does not
1006        matter */
1007 
1008     ul_width = XTextWidth( fs, NARROW( ch ) ? "h" : "D", 1 );
1009     ul_rwidth = XTextWidth( fs, str + n, 1 );
1010 
1011     pre = str[ 0 ] == *fl_ul_magic_char;
1012 
1013     xoff = fli_get_string_widthTABfs( fs, str + pre, n - pre );
1014 
1015     /* Try to center the underline on the correct character */
1016 
1017     if ( UL_propwidth )
1018         x = x + xoff;
1019     else
1020         x = x + xoff + ( ul_rwidth - ul_width ) / 2;
1021 
1022     xr.x = x;
1023     xr.y = y + ul_pos;
1024     xr.width = UL_propwidth ? ul_rwidth : ul_width;
1025     xr.height = ul_thickness;
1026     return &xr;
1027 }
1028 
1029 
1030 /***************************************
1031  ***************************************/
1032 
1033 static void
do_underline(FL_Coord x,FL_Coord y,const char * cstr,int n)1034 do_underline( FL_Coord     x,
1035               FL_Coord     y,
1036               const char * cstr,
1037               int          n )
1038 {
1039     XRectangle *xr = fli_get_underline_rect( flx->fs, x, y, cstr, n );
1040 
1041     if ( flx->win == None || xr->width <= 0 ||  xr->height <= 0 )
1042         return;
1043 
1044     XFillRectangle( flx->display, flx->win, flx->gc, xr->x, xr->y,
1045                     xr->width, xr->height );
1046 }
1047 
1048 
1049 #define has_desc( s )    (    strchr( s, 'g' )  \
1050                            || strchr( s, 'j' )  \
1051                            || strchr( s, 'q' )  \
1052                            || strchr( s, 'y' )  \
1053                            || strchr( s, 'p' ) )
1054 
1055 
1056 /***************************************
1057  * Underline whole string
1058  ***************************************/
1059 
1060 static void
do_underline_all(FL_Coord x,FL_Coord y,const char * str,int n,unsigned long * ul_pos,unsigned long * ul_thickness)1061 do_underline_all( FL_Coord        x,
1062                   FL_Coord        y,
1063                   const char    * str,
1064                   int             n,
1065                   unsigned long * ul_pos,
1066                   unsigned long * ul_thickness )
1067 {
1068     int ul_width;
1069 
1070     if ( flx->win == None )
1071         return;
1072 
1073     if ( UL_thickness < 0 )
1074         XGetFontProperty( flx->fs, XA_UNDERLINE_THICKNESS, ul_thickness );
1075     else
1076         *ul_thickness = UL_thickness;
1077 
1078     if ( *ul_thickness == 0 || *ul_thickness > 100 )
1079         *ul_thickness = strstr( fli_curfnt, "bold" ) ? 2 : 1;
1080 
1081     if ( ! XGetFontProperty( flx->fs, XA_UNDERLINE_POSITION, ul_pos ) )
1082         *ul_pos = has_desc( str ) ? ( 1 + flx->fdesc ) : 1;
1083 
1084     ul_width = XTextWidth( flx->fs, str, n );
1085 
1086     /* Draw it */
1087 
1088     if ( ul_width > 0 && *ul_thickness > 0 )
1089         XFillRectangle( flx->display, flx->win, flx->gc, x, y + *ul_pos,
1090                         ul_width, *ul_thickness );
1091 }
1092 
1093 
1094 /***************************************
1095  * Draw a single line string possibly with embedded tabs
1096  ***************************************/
1097 
1098 int
fli_draw_stringTAB(Window win,GC gc,int x,int y,int style,int size,const char * s,int len,int img)1099 fli_draw_stringTAB( Window       win,
1100                     GC           gc,
1101                     int          x,
1102                     int          y,
1103                     int          style,
1104                     int          size,
1105                     const char * s,
1106                     int          len,
1107                     int          img )
1108 {
1109     int w, tab;
1110     const char *p,
1111                *q;
1112     XFontStruct *fs = fl_get_font_struct( style, size );
1113     DrawString drawIt = img ? XDrawImageString : XDrawString;
1114 
1115     if ( win == 0 )
1116         return 0;
1117 
1118     tab = fli_get_tabpixels( fs );
1119 
1120     XSetFont( flx->display, gc, fs->fid );
1121 
1122     for ( w = 0, q = s; *q && ( p = strchr( q, '\t' ) ) && p - s < len;
1123           q = p + 1 )
1124     {
1125         drawIt( flx->display, win, gc, x + w, y, ( char * ) q, p - q );
1126         w += XTextWidth( fs, q, p - q );
1127         w = ( w / tab + 1 ) * tab;
1128     }
1129 
1130     drawIt( flx->display, win, gc, x + w, y, ( char * ) q, s - q + len );
1131 
1132     return 0;
1133 }
1134 
1135 
1136 /*
1137  * Local variables:
1138  * tab-width: 4
1139  * indent-tabs-mode: nil
1140  * End:
1141  */
1142