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 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "include/forms.h"
24 #include "flinternal.h"
25 #include "private/pbrowser.h"
26 
27 
28 #include <string.h>
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 
33 
34 #define TOP_MARGIN     1
35 #define RIGHT_MARGIN   2
36 #define BOTTOM_MARGIN  1
37 #define LEFT_MARGIN    3     /* must be at least 1 for selection box */
38 
39 
40 static int handle_tbox( FL_OBJECT *,
41                         int,
42                         FL_Coord,
43                         FL_Coord,
44                         int,
45                         void * );
46 
47 static GC create_gc( FL_OBJECT *,
48                      int,
49                      int,
50                      FL_COLOR,
51                      int,
52                      int,
53                      int,
54                      int  );
55 
56 
57 /***************************************
58  * Creates a new textbox object
59  ***************************************/
60 
61 FL_OBJECT *
fli_create_tbox(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)62 fli_create_tbox( int          type,
63                  FL_Coord     x,
64                  FL_Coord     y,
65                  FL_Coord     w,
66                  FL_Coord     h,
67                  const char * label )
68 {
69     FL_OBJECT *obj;
70     FLI_TBOX_SPEC *sp;
71 
72     obj = fl_make_object( FL_TBOX, type, x, y, w, h, label, handle_tbox );
73 
74     obj->boxtype      = FLI_TBOX_BOXTYPE;
75     obj->lcol         = FLI_TBOX_LCOL;
76     obj->col1         = FLI_TBOX_COL1;
77     obj->col2         = FLI_TBOX_COL2;
78     obj->align        = FLI_TBOX_ALIGN;
79     obj->wantkey      = FL_KEY_SPECIAL;
80     obj->want_update  = 0;
81     obj->spec         = sp = fl_malloc( sizeof *sp );
82 
83     sp->x             = 0;
84     sp->y             = 0;
85     sp->w             = 0;
86     sp->h             = 0;
87     sp->attrib        = 1;
88     sp->no_redraw     = 0;
89     sp->lines         = NULL;
90     sp->num_lines     = 0;
91     sp->callback      = NULL;
92     sp->xoffset       = 0;
93     sp->yoffset       = 0;
94     sp->max_width     = 0;
95     sp->max_height    = 0;
96     sp->def_size      = fli_cntl.browserFontSize ?
97                         fli_cntl.browserFontSize : FLI_TBOX_FONTSIZE;
98     sp->def_style     = FL_NORMAL_STYLE;
99     sp->def_align     = FL_ALIGN_LEFT;
100     sp->def_height    = 0;
101     sp->defaultGC     = None;
102     sp->backgroundGC  = None;
103     sp->selectGC      = None;
104     sp->nonselectGC   = None;
105     sp->bw_selectGC   = None;
106     sp->specialkey    = '@';
107     sp->select_line   = -1;
108     sp->deselect_line = -1;
109     sp->react_to_vert = sp->react_to_hori = 1;
110 
111     /* Per default the object never gets returned, user must change that */
112 
113     fl_set_object_return( obj, FL_RETURN_NONE );
114 
115     return obj;
116 }
117 
118 
119 /***************************************
120  * Deletes a line from the textbox
121  ***************************************/
122 
123 void
fli_tbox_delete_line(FL_OBJECT * obj,int line)124 fli_tbox_delete_line( FL_OBJECT * obj,
125                       int         line )
126 {
127     FLI_TBOX_SPEC *sp = obj->spec;
128     int recalc_max_width = 0;
129     int i;
130 
131     /* If line number is invalid do nothing */
132 
133     if ( line < 0 || line >= sp->num_lines )
134         return;
135 
136     if ( sp->select_line == line )
137         sp->select_line = -1;
138     else if ( sp->select_line > line )
139         sp->select_line--;
140 
141     if ( sp->deselect_line == line )
142         sp->deselect_line = -1;
143     else if ( sp->deselect_line > line )
144         sp->deselect_line--;
145 
146     /* Check if recalculation of maximum line length is necessary */
147 
148     if ( sp->max_width == sp->lines[ line ]->w )
149         recalc_max_width = 1;
150 
151     /* Set vertical position of following lines */
152 
153     for ( i = line + 1; i < sp->num_lines; i++ )
154         sp->lines[ i ]->y -= sp->lines[ line ]->h;
155 
156     sp->max_height -= sp->lines[ line ]->h;
157 
158     /* Get rid of special GC for the line */
159 
160     if ( sp->lines[ line ]->specialGC )
161     {
162         XFreeGC( flx->display, sp->lines[ line ]->specialGC );
163         sp->lines[ line ]->specialGC = None;
164     }
165 
166     /* Deallocate memory for the text of the line to delete */
167 
168     fli_safe_free( sp->lines[ line ]->fulltext );
169 
170     /* Get rid of memory for the structure */
171 
172     fl_free( sp->lines[ line ] );
173 
174     /* Move pointers to following line structures  one up */
175 
176     if ( --sp->num_lines != line )
177         memmove( sp->lines + line, sp->lines + line + 1,
178                  ( sp->num_lines - line ) * sizeof *sp->lines );
179 
180     /* Reduce memory for array of structure pointers */
181 
182     sp->lines = fl_realloc( sp->lines, sp->num_lines * sizeof *sp->lines );
183 
184     /* If necessary find remaining longest line */
185 
186     if ( recalc_max_width )
187     {
188         sp->max_width = 0;
189         for ( i = 0; i < sp->num_lines; i++ )
190             sp->max_width = FL_max( sp->max_width, sp->lines[ i ]->w );
191 
192         /* Correct x offset if necessary */
193 
194         if ( sp->max_width <= sp->w )
195             sp->xoffset = 0;
196         else if ( sp->xoffset > sp->max_width - sp->w )
197             sp->xoffset = sp->max_width - sp->w;
198     }
199 
200     /* Check that offset is still reasonable */
201 
202     if ( sp->num_lines == 0 )
203         sp->yoffset = 0;
204     else if (   sp->lines[ sp->num_lines - 1 ]->y
205               + sp->lines[ sp->num_lines - 1 ]->h < sp->yoffset + sp->h )
206     {
207         int old_no_redraw = sp->no_redraw;
208 
209         sp->no_redraw = 1;
210         fli_tbox_set_bottomline( obj, sp->num_lines - 1 );
211         sp->no_redraw = old_no_redraw;
212     }
213 
214     if ( ! sp->no_redraw )
215         fl_redraw_object( obj );
216 }
217 
218 
219 /***************************************
220  * Inserts one or more lines, separated by
221  * linefeed characters, into the textbox
222  ***************************************/
223 
224 void
fli_tbox_insert_lines(FL_OBJECT * obj,int line,const char * new_text)225 fli_tbox_insert_lines( FL_OBJECT  * obj,
226                        int          line,
227                        const char * new_text )
228 {
229     char *text = fl_strdup( new_text );
230     char *p = text;
231     char *del;
232 
233     while ( 1 )
234     {
235         ;
236         if ( ( del = strchr( p, '\n' ) ) )
237             *del = '\0';
238         fli_tbox_insert_line( obj, line++, p );
239         if ( del )
240             p = del + 1;
241         else
242             break;
243     }
244 
245     fl_free( text );
246 }
247 
248 
249 /***************************************
250  * Inserts a single line into the textbox
251  ***************************************/
252 
253 void
fli_tbox_insert_line(FL_OBJECT * obj,int line,const char * new_text)254 fli_tbox_insert_line( FL_OBJECT  * obj,
255                       int          line,
256                       const char * new_text )
257 {
258     FLI_TBOX_SPEC *sp = obj->spec;
259     char *text;
260     char *p;
261     int done = 0;
262     char *e;
263     int is_bold = 0;
264     int is_italic = 0;
265     TBOX_LINE *tl;
266     int i;
267 
268     /* Catch invalid 'line' or 'new_text' argument */
269 
270     if ( line < 0 || ! new_text )
271         return;
272 
273     /* If 'line' is too large correct that by appending to the end */
274 
275     if ( line >= sp->num_lines )
276         line = sp->num_lines;
277 
278     /* Make sure the lines marked as selected and deselected remain unchanged */
279 
280     if ( sp->select_line >= line )
281         sp->select_line++;
282     if ( sp->deselect_line >= line )
283         sp->deselect_line++;
284 
285     /* Make a copy of the text of the line */
286 
287     p = text = strdup( new_text );
288 
289     /* Get memory for one more line */
290 
291     sp->lines = fl_realloc( sp->lines,
292                             ++sp->num_lines * sizeof *sp->lines );
293 
294     /* If necessary move all following lines one down */
295 
296     if ( line < sp->num_lines - 1 )
297         memmove( sp->lines + line + 1, sp->lines + line,
298                  ( sp->num_lines - line - 1 ) * sizeof *sp->lines );
299 
300     sp->lines[ line ] = tl = fl_malloc( sizeof **sp->lines );
301 
302     /* Set up defaults for the line */
303 
304     tl->fulltext      = NULL;
305     tl->text          = NULL;
306     tl->len           = 0;
307     tl->selected      = 0;
308     tl->selectable    = 1;
309     tl->is_separator  = 0;
310     tl->is_underlined = 0;
311     tl->x             = 0;
312     tl->w             = 0;
313     tl->h             = sp->def_size;
314     tl->size          = sp->def_size;
315     tl->style         = sp->def_style;
316     tl->align         = sp->def_align;
317     tl->color         = obj->lcol;
318     tl->is_special    = 0;
319     tl->specialGC     = None;
320     tl->incomp_esc    = 0;
321 
322     /* Check for flags at the start of the line. When we're done 'p' will
323        points to the start of the string to be shown in the textbox. */
324 
325     while ( *p && *p == sp->specialkey && ! done )
326     {
327         if ( p[ 1 ] == sp->specialkey )
328         {
329                 p += 1;
330                 break;
331         }
332 
333         switch ( p [ 1 ] )
334         {
335             case '\0' :
336                 tl->incomp_esc = 1;
337                 done = 1;
338                 break;
339 
340             case 'h' :
341                 tl->size = FL_HUGE_SIZE;
342                 p += 2;
343                 break;
344 
345             case 'l' :
346                 tl->size = FL_LARGE_SIZE;
347                 p += 2;
348                 break;
349 
350             case 'm' :
351                 tl->size = FL_MEDIUM_SIZE;
352                 p += 2;
353                 break;
354 
355             case 's' :
356                 tl->size = FL_SMALL_SIZE;
357                 p += 2;
358                 break;;
359 
360             case 'L' :
361                 tl->size += 6;
362                 p += 2;
363                 break;
364 
365             case 'M' :
366                 tl->size += 4;
367                 p += 2;
368                 break;
369 
370             case 'S' :
371                 tl->size -= 2;
372                 p += 2;
373                 break;
374 
375             case 'b' :
376                 tl->style |= FL_BOLD_STYLE;
377                 is_bold = 1;
378                 p += 2;
379                 break;
380 
381             case 'i' :
382                 tl->style |= FL_ITALIC_STYLE;
383                 is_italic = 1;
384                 p += 2;
385                 break;
386 
387             case 'n' :
388                 tl->style = FL_NORMAL_STYLE;
389                 if ( is_bold )
390                     tl->style |= FL_BOLD_STYLE;
391                 if ( is_italic )
392                     tl->style |= FL_ITALIC_STYLE;
393                 p += 2;
394                 break;
395 
396             case 'f' :
397                 tl->style = FL_FIXED_STYLE;
398                 if ( is_bold )
399                     tl->style |= FL_BOLD_STYLE;
400                 if ( is_italic )
401                     tl->style |= FL_ITALIC_STYLE;
402                 p += 2;
403                 break;
404 
405             case 't' :
406                 tl->style = FL_TIMES_STYLE;
407                 if ( is_bold )
408                     tl->style |= FL_BOLD_STYLE;
409                 if ( is_italic )
410                     tl->style |= FL_ITALIC_STYLE;
411                 p += 2;
412                 break;
413 
414             case 'c' :
415                 tl->align = FL_ALIGN_CENTER;
416                 p += 2;
417                 break;
418 
419             case 'r' :
420                 tl->align = FL_ALIGN_RIGHT;
421                 p += 2;
422                 break;
423 
424             case '_' :
425                 tl->is_underlined = 1;
426                 p += 2;
427                 break;
428 
429             case '-' :
430                 sp->lines[ line ]->is_separator = 1;
431                 sp->lines[ line ]->selectable   = 0;
432                 done = 1;
433                 break;
434 
435             case 'N' :
436                 sp->lines[ line ]->selectable = 0;
437                 tl->color = FL_INACTIVE;
438                 p += 2;
439                 break;
440 
441             case 'C' :
442                 tl->color = strtol( p + 2, &e, 10 );
443                 if ( e == p + 2 )
444                 {
445                     if ( p[ 2 ] == '\0' )
446                         tl->incomp_esc = 1;
447                     else
448                         M_err( "fli_tbox_insert_line", "missing color" );
449                     p += 1;
450                     break;
451                 }
452 
453                 if ( tl->color >= FL_MAX_COLS )
454                 {
455                     M_err( "fli_tbox_insert_line", "bad color %ld", tl->color );
456                     tl->color = obj->lcol;
457                 }
458                 p = e;
459                 break;
460 
461             case ' ' :
462                 p += 2;
463                 break;
464 
465             default :
466                 M_err( "fli_tbox_insert_line", "bad flag %c", p[ 1 ] );
467                 p += 1;
468                 done = 1;
469                 break;
470         }
471     }
472 
473     tl->fulltext = text;
474     if ( ! tl->is_separator )
475         tl->text = p;
476     else
477         tl->text = tl->fulltext + strlen( tl->fulltext );
478 
479     tl->len = strlen( tl->text );
480 
481     /* Figure out width and height of string */
482 
483     if ( ! tl->is_separator && *tl->text )
484     {
485         tl->w = fl_get_string_widthTAB( tl->style, tl->size,
486                                         tl->text, tl->len );
487         tl->h = fl_get_string_height( tl->style, tl->size,
488                                       tl->len ? tl->text : " " , tl->len | 1,
489                                       &tl->asc, &tl->desc );
490     }
491     else
492     {
493         tl->w = 0;
494         tl->h = fl_get_string_height( tl->style, tl->size,
495                                       "X", 1, &tl->asc, &tl->desc );
496     }
497 
498     /* If the new line is longer than all others we need to recalculate the
499        horizontal position of all lines that aren't left aligned */
500 
501     if ( tl->w > sp->max_width )
502     {
503         sp->max_width = tl->w;
504         for ( i = 0; i < sp->num_lines; i++ )
505             if ( fl_is_center_lalign( sp->lines[ i ]->align ) )
506                 sp->lines[ i ]->x = ( sp->max_width - sp->lines[ i ]->w ) / 2;
507             else if ( fl_to_outside_lalign( sp->lines[ i ]->align ) ==
508                                                              FL_ALIGN_RIGHT )
509                 sp->lines[ i ]->x = sp->max_width - sp->lines[ i ]->w;
510     }
511     else
512     {
513         if ( fl_is_center_lalign( tl->align ) )
514             tl->x = ( sp->max_width - tl->w ) / 2;
515         else if ( fl_to_outside_lalign( tl->align ) == FL_ALIGN_RIGHT )
516             tl->x = sp->max_width - tl->w;
517     }
518 
519     /* Calculate the vertical position of the line, shifting that of lines
520        that may come afterwards */
521 
522     if ( sp->num_lines == 1 )
523         tl->y = 0;
524     else if ( line == sp->num_lines - 1 )
525         tl->y = sp->lines[ line - 1 ]->y + sp->lines[ line - 1 ]->h;
526     else
527     {
528         tl->y = sp->lines[ line + 1 ]->y;
529         for ( i = line + 1; i < sp->num_lines; i++ )
530             sp->lines[ i ]->y += tl->h;
531     }
532 
533     sp->max_height += tl->h;
534 
535     /* Set flag if the line isn't to be drawn in default style, size and
536        color. We don't create a GC yet since this might be called before
537        the textbox is visible! */
538 
539     if (    tl->style != sp->def_style
540          || tl->size  != sp->def_size
541          || ( tl->color != obj->lcol && tl->selectable ) )
542         tl->is_special = 1;
543 
544     if ( ! sp->no_redraw )
545         fl_redraw_object( obj );
546 }
547 
548 
549 /***************************************
550  * Appends a line to the end of the textbox
551  ***************************************/
552 
553 void
fli_tbox_add_line(FL_OBJECT * obj,const char * text,int show)554 fli_tbox_add_line( FL_OBJECT  * obj,
555                    const char * text,
556                    int          show )
557 {
558    FLI_TBOX_SPEC *sp = obj->spec;
559 
560    fli_tbox_insert_lines( obj, sp->num_lines, text );
561 
562    /* Make last line visible if asked for */
563 
564    if ( show && sp->num_lines )
565    {
566        TBOX_LINE *tl = sp->lines[ sp->num_lines - 1 ];
567 
568        if ( tl->y + tl->h - sp->yoffset >= sp->h )
569            fli_tbox_set_bottomline( obj, sp->num_lines - 1 );
570    }
571 }
572 
573 
574 /**************************************
575  * Appends characters to the last line
576  * in the textbox
577  **************************************/
578 
579 void
fli_tbox_add_chars(FL_OBJECT * obj,const char * add)580 fli_tbox_add_chars( FL_OBJECT  * obj,
581                     const char * add )
582 {
583     FLI_TBOX_SPEC *sp = obj->spec;
584     TBOX_LINE *tl;
585     int new_len;
586     char *old_fulltext;
587     char * old_text;
588     char *new_text;
589     char *del;
590 
591     /* If there's nothing to add return */
592 
593     if ( ! add || ! *add )
594         return;
595 
596     /* If there aren't any lines yet it's equivalent to inserting a new one */
597 
598     if ( sp->num_lines == 0 )
599     {
600         fli_tbox_insert_lines( obj, sp->num_lines, add );
601         return;
602     }
603 
604     tl = sp->lines[ sp->num_lines - 1 ];
605 
606     /* If there's no text or the line has an incomplete escape sequence that
607        possibly could become completed due to the new text assemble the text
608        of the line from the old text and the new text, delete te old line and
609        then draw a new one in its place.
610 
611        Another question is how to deal with situations where, by a previous
612        call with e.g. "@C3", a color was selected and in the next call the
613        string to be added starts with a digit, let's say '7'. One possibility
614        would be to re-evaluate the combined string to mean "@C37", setting
615        a different color. But since in older versions this didn't happen
616        (and some users may rely on this), it is assumed that this isn't the
617        users intention and to avoid the two digits to become collated and
618        interpreted as the number of a different color "@ " is inserted in
619        between the digits, with "@ " treated as a separator between digits
620        (the intended use of "@ " is to allow lines like "@C3@ 2. Chapter", i.
621        e. having a color specification, followed by printable text that starts
622        with a digit). */
623 
624     if ( tl->len == 0 || tl->incomp_esc )
625     {
626         int old_no_redraw = sp->no_redraw;
627         size_t old_len = strlen( tl->fulltext );
628         size_t len = strlen( add ) + 1;
629         int insert = tl->len == 0
630                      && old_len > 0
631                      && isdigit( tl->fulltext[ old_len - 1 ] )
632                      && isdigit( *add ) ? 2 : 0;
633 
634         new_text = fl_malloc( old_len + len + insert );
635         if ( old_len )
636         {
637             memcpy( new_text, tl->fulltext, old_len );
638             if ( insert )
639                 memcpy( new_text + old_len, "@ ", 2 );
640         }
641         memcpy( new_text + old_len + insert, add, len );
642         sp->no_redraw = 1;
643 
644         fli_tbox_delete_line( obj, sp->num_lines - 1 );
645         fli_tbox_insert_lines( obj, sp->num_lines, new_text );
646         sp->no_redraw = old_no_redraw;
647         fl_free( new_text );
648         return;
649     }
650 
651     /* Append everything to the last line up to a linefeed */
652 
653     if ( ( del = strchr( add, '\n' ) ) )
654     {
655         new_text = fl_malloc( del - add + 1 );
656         memcpy( new_text, add, del - add );
657         new_text[ del - add ] = '\0';
658     }
659     else
660         new_text = ( char * ) add;
661 
662     /* Make up the new text of the line from the old and the new text */
663 
664     new_len = strlen( tl->fulltext ) + strlen( new_text ) + 1;
665     old_text = tl->text;
666     old_fulltext = tl->fulltext;
667 
668     tl->fulltext = fl_malloc( new_len + 1 );
669     strcpy( tl->fulltext, old_fulltext );
670     strcat( tl->fulltext, new_text );
671     tl->text = tl->fulltext + ( old_text - old_fulltext );
672     tl->len = strlen( tl->text ); //new_len;
673 
674     fli_safe_free( old_fulltext );
675 
676     /* Text of a separator line never gets shown */
677 
678     if ( tl->is_separator )
679         return;
680 
681     /* Figure out the new length of the line */
682 
683     if ( *tl->text )
684         tl->w = fl_get_string_widthTAB( tl->style, tl->size,
685                                         tl->text, tl->len );
686 
687     /* If line is now longer than all others we need to recalculate the
688        horizontal position of all lines that aren't left aligned */
689 
690     if ( tl->w > sp->max_width )
691     {
692         int i;
693 
694         sp->max_width = tl->w;
695         for ( i = 0; i < sp->num_lines; i++ )
696             if ( fl_is_center_lalign( sp->lines[ i ]->align ) )
697                 sp->lines[ i ]->x = ( sp->max_width - sp->lines[ i ]->w ) / 2;
698             else if ( fl_to_outside_lalign( sp->lines[ i ]->align ) ==
699                                                               FL_ALIGN_RIGHT )
700                 sp->lines[ i ]->x = sp->max_width - sp->lines[ i ]->w;
701     }
702     else
703     {
704         if ( fl_is_center_lalign( tl->align ) )
705             tl->x = ( sp->max_width - tl->w ) / 2;
706         else if ( fl_to_outside_lalign( tl->align ) == FL_ALIGN_RIGHT )
707             tl->x = sp->max_width - tl->w;
708     }
709 
710     /* If there was no newline in the string to be appended we're done,
711        otherwise the remaining stuff has to be added as new lines */
712 
713     if ( ! del )
714     {
715        TBOX_LINE *tl = sp->lines[ sp->num_lines - 1 ];
716 
717        if ( tl->y + tl->h - sp->yoffset >= sp->h )
718            fli_tbox_set_bottomline( obj, sp->num_lines - 1 );
719     }
720     else
721     {
722         fl_free( new_text );
723         fli_tbox_add_line( obj, del + 1, 1 );
724     }
725 }
726 
727 
728 /*********************************
729  * Replaces a line in the textbox
730  *********************************/
731 
732 void
fli_tbox_replace_line(FL_OBJECT * obj,int line,const char * text)733 fli_tbox_replace_line( FL_OBJECT  * obj,
734                        int          line,
735                        const char * text )
736 {
737    FLI_TBOX_SPEC *sp = obj->spec;
738    int old_select_line = sp->select_line;
739    int old_no_redraw = sp->no_redraw;
740 
741    if ( line < 0 || line >= sp->num_lines || ! text )
742        return;
743 
744    sp->no_redraw = 1;
745    fli_tbox_delete_line( obj, line );
746    sp->no_redraw = old_no_redraw;
747    fli_tbox_insert_line( obj, line, text );
748    if ( line == old_select_line && sp->lines[ line ]->selectable )
749        fli_tbox_select_line( obj, line );
750 }
751 
752 
753 /*************************************
754  * Removes all lines from the textbox
755  *************************************/
756 
757 void
fli_tbox_clear(FL_OBJECT * obj)758 fli_tbox_clear( FL_OBJECT * obj )
759 {
760     FLI_TBOX_SPEC *sp = obj->spec;
761     int i;
762 
763     sp->select_line = sp->deselect_line = -1;
764 
765     if ( sp->num_lines == 0 )
766         return;
767 
768     for ( i = 0; i < sp->num_lines; i++ )
769     {
770         if ( sp->lines[ i ]->specialGC )
771         {
772             XFreeGC( flx->display, sp->lines[ i ]->specialGC );
773             sp->lines[ i ]->specialGC = None;
774         }
775         fli_safe_free( sp->lines[ i ]->fulltext );
776         fli_safe_free( sp->lines[ i ] );
777     }
778 
779     fli_safe_free( sp->lines );
780 
781     sp->num_lines  = 0;
782     sp->max_width  = 0;
783     sp->max_height = 0;
784     sp->xoffset    = 0;
785     sp->yoffset    = 0;
786 
787     if ( ! sp->no_redraw )
788         fl_redraw_object( obj );
789 }
790 
791 
792 /***********************************************
793  * Loads all lines from a file into the textbox
794  ***********************************************/
795 
796 int
fli_tbox_load(FL_OBJECT * obj,const char * filename)797 fli_tbox_load( FL_OBJECT  * obj,
798                const char * filename )
799 {
800     FLI_TBOX_SPEC *sp = obj->spec;
801     FILE *fp;
802     char *text;
803     char *del;
804 
805     /* Load the file */
806 
807     if ( ! filename || ! *filename )
808         return 0;
809 
810     if ( ! ( fp = fopen( filename, "r" ) ) )
811         return 0;
812 
813     while ( ( text = fli_read_line( fp ) ) && *text )
814     {
815         int old_no_redraw = sp->no_redraw;
816 
817         /* Get rid of linefeed at end of line */
818 
819         if ( ( del = strrchr( text, '\n' ) ) )
820             *del = '\0';
821 
822         sp->no_redraw = 1;
823         fli_tbox_insert_line( obj, sp->num_lines, text );
824         sp->no_redraw = old_no_redraw;
825         fl_free( text );
826     }
827 
828     fli_safe_free( text );
829 
830     fclose( fp );
831 
832     if ( ! sp->no_redraw )
833         fl_redraw_object( obj );
834 
835     return 1;
836 }
837 
838 
839 /************************************
840  * Returns the text of a line in the
841  * textbox (including flags)
842  ************************************/
843 
844 const char *
fli_tbox_get_line(FL_OBJECT * obj,int line)845 fli_tbox_get_line( FL_OBJECT * obj,
846                    int         line )
847 {
848    FLI_TBOX_SPEC *sp = obj->spec;
849 
850    if ( line < 0 || line >= sp->num_lines )
851        return NULL;
852 
853    return sp->lines[ line ]->fulltext;
854 }
855 
856 
857 /*************************************
858  * Sets a new font size for all lines
859  * drawn with default settings
860  *************************************/
861 
862 void
fli_tbox_set_fontsize(FL_OBJECT * obj,int size)863 fli_tbox_set_fontsize( FL_OBJECT * obj,
864                        int         size )
865 {
866     FLI_TBOX_SPEC *sp = obj->spec;
867     double old_xrel;
868     double old_yrel;
869     int old_no_redraw = sp->no_redraw;
870     int i;
871 
872     if ( size < FL_TINY_SIZE || size > FL_HUGE_SIZE )
873         return;
874 
875     sp->def_size = size;
876 
877     sp->attrib = 1;
878 
879     if ( sp->num_lines == 0 )
880         return;
881 
882     old_xrel = fli_tbox_get_rel_xoffset( obj );
883     old_yrel = fli_tbox_get_rel_yoffset( obj );
884 
885     /* Calculate width and height for all lines */
886 
887     for ( i = 0; i < sp->num_lines; i++ )
888     {
889         TBOX_LINE *tl = sp->lines[ i ];
890 
891         if ( tl->is_special )
892             continue;
893 
894         tl->size = size;
895 
896         /* Figure out width and height of string */
897 
898         if ( ! tl->is_separator && *tl->text )
899         {
900             tl->w = fl_get_string_widthTAB( tl->style, tl->size,
901                                             tl->text, tl->len );
902             tl->h = fl_get_string_height( tl->style, tl->size,
903                                           tl->len ? tl->text : " ",
904                                           tl->len | 1,
905                                           &tl->asc, &tl->desc );
906         }
907         else
908         {
909             tl->w = 0;
910             tl->h = fl_get_string_height( tl->style, tl->size,
911                                           "X", 1, &tl->asc, &tl->desc );
912         }
913     }
914 
915     /* Calculate vertical positions of all lines and maximum width */
916 
917     sp->max_width = sp->lines[ 0 ]->w;
918 
919     for ( i = 1; i < sp->num_lines; i++ )
920     {
921         sp->lines[ i ]->y = sp->lines[ i - 1 ]->y + sp->lines[ i - 1 ]->h;
922         sp->max_width = FL_max( sp->max_width, sp->lines[ i ]->w );
923     }
924 
925     /* Determine new height of all the text */
926 
927     sp->max_height =   sp->lines[ sp->num_lines - 1 ]->y
928                      + sp->lines[ sp->num_lines - 1 ]->h;
929 
930     sp->no_redraw = 1;
931     fli_tbox_set_rel_xoffset( obj, old_xrel );
932     fli_tbox_set_rel_yoffset( obj, old_yrel );
933     sp->no_redraw = old_no_redraw;
934 }
935 
936 
937 /**************************************
938  * Sets a new font style for all lines
939  * drawn with default settings
940  **************************************/
941 
942 void
fli_tbox_set_fontstyle(FL_OBJECT * obj,int style)943 fli_tbox_set_fontstyle( FL_OBJECT * obj,
944                         int         style )
945 {
946     FLI_TBOX_SPEC *sp = obj->spec;
947     double old_xrel;
948     double old_yrel;
949     int old_no_redraw = sp->no_redraw;
950     int i;
951 
952     if ( style < FL_NORMAL_STYLE || style > FL_TIMESBOLDITALIC_STYLE )
953         return;
954 
955     sp->def_style = style;
956 
957     sp->attrib = 1;
958 
959     if ( sp->num_lines == 0 )
960         return;
961 
962     old_xrel = fli_tbox_get_rel_xoffset( obj );
963     old_yrel = fli_tbox_get_rel_yoffset( obj );
964 
965     /* Calculate width and height for all lines */
966 
967     for ( i = 0; i < sp->num_lines; i++ )
968     {
969         TBOX_LINE *tl = sp->lines[ i ];
970 
971         if ( tl->is_special )
972             continue;
973 
974         tl->style = style;
975 
976         /* Figure out width and height of string */
977 
978         if ( ! tl->is_separator && *tl->text )
979         {
980             tl->w = fl_get_string_widthTAB( tl->style, tl->size,
981                                             tl->text, tl->len );
982             tl->h = fl_get_string_height( tl->style, tl->size,
983                                           tl->len ? tl->text : " ",
984                                           tl->len | 1,
985                                           &tl->asc, &tl->desc );
986         }
987         else
988         {
989             tl->w = 0;
990             tl->h = fl_get_string_height( tl->style, tl->size,
991                                           "X", 1, &tl->asc, &tl->desc );
992         }
993     }
994 
995     /* Calculate vertical positions of all lines and the width of the
996        longest line */
997 
998     sp->max_width = sp->lines[ 0 ]->w;
999 
1000     for ( i = 1; i < sp->num_lines; i++ )
1001     {
1002         sp->lines[ i ]->y = sp->lines[ i - 1 ]->y + sp->lines[ i - 1 ]->h;
1003         sp->max_width = FL_max( sp->max_width, sp->lines[ i ]->w );
1004     }
1005 
1006     /* Determine new height of total text */
1007 
1008     sp->max_height =   sp->lines[ sp->num_lines - 1 ]->y
1009                      + sp->lines[ sp->num_lines - 1 ]->h;
1010 
1011     sp->attrib = 1;
1012 
1013     sp->no_redraw = 1;
1014     fli_tbox_set_rel_xoffset( obj, old_xrel );
1015     fli_tbox_set_rel_yoffset( obj, old_yrel );
1016     sp->no_redraw = old_no_redraw;
1017 }
1018 
1019 
1020 /*************************************
1021  * Sets the x-offset in pixels of the
1022  * text displayed in the textbox
1023  *************************************/
1024 
1025 int
fli_tbox_set_xoffset(FL_OBJECT * obj,int pixel)1026 fli_tbox_set_xoffset( FL_OBJECT * obj,
1027                       int         pixel )
1028 {
1029     FLI_TBOX_SPEC *sp = obj->spec;
1030 
1031     if ( sp->max_width <= sp->w || pixel < 0 )
1032         pixel = 0;
1033     if ( pixel > sp->max_width - sp->w )
1034         pixel = FL_max( 0, sp->max_width - sp->w );
1035 
1036     sp->xoffset = pixel;
1037 
1038     if ( ! sp->no_redraw )
1039         fl_redraw_object( obj );
1040 
1041     return pixel;
1042 }
1043 
1044 
1045 /***************************************
1046  * Sets the x-offset of the text displayed in the textbox as a
1047  * number between 0 (starts of lines are shown) and 1 (end of
1048  * longest line is shown)
1049  ***************************************/
1050 
1051 double
fli_tbox_set_rel_xoffset(FL_OBJECT * obj,double offset)1052 fli_tbox_set_rel_xoffset( FL_OBJECT * obj,
1053                           double      offset )
1054 {
1055     FLI_TBOX_SPEC *sp = obj->spec;
1056 
1057     if ( sp->max_width <= sp->w || offset < 0.0 )
1058         offset = 0.0;
1059     if ( offset > 1.0 )
1060         offset = 1.0;
1061 
1062     sp->xoffset = FL_nint( offset * FL_max( 0, sp->max_width - sp->w ) );
1063 
1064     if ( ! sp->no_redraw )
1065         fl_redraw_object( obj );
1066 
1067     return fli_tbox_get_rel_xoffset( obj );
1068 }
1069 
1070 
1071 /***************************************
1072  * Sets the y-offset in pixels of the text displayed in the textbox
1073  ***************************************/
1074 
1075 int
fli_tbox_set_yoffset(FL_OBJECT * obj,int pixel)1076 fli_tbox_set_yoffset( FL_OBJECT * obj,
1077                       int         pixel )
1078 {
1079     FLI_TBOX_SPEC *sp = obj->spec;
1080 
1081     if ( sp->max_height <= sp->h || pixel < 0 )
1082         pixel = 0;
1083     if ( pixel > sp->max_height - sp->h )
1084         pixel = FL_max( 0, sp->max_height - sp->h );
1085 
1086     sp->yoffset = pixel;
1087 
1088     if ( ! sp->no_redraw )
1089         fl_redraw_object( obj );
1090 
1091     return pixel;
1092 }
1093 
1094 
1095 /***************************************
1096  * Sets the y-offset of the text displayed in the textbox as a
1097  * number between 0 (show start of text) and 1 (show end of text)
1098  ***************************************/
1099 
1100 double
fli_tbox_set_rel_yoffset(FL_OBJECT * obj,double offset)1101 fli_tbox_set_rel_yoffset( FL_OBJECT * obj,
1102                           double      offset )
1103 {
1104     FLI_TBOX_SPEC *sp = obj->spec;
1105 
1106     if ( sp->max_height <= sp->h || offset < 0.0 )
1107         offset = 0.0;
1108     if ( offset > 1.0 )
1109         offset = 1.0;
1110 
1111     sp->yoffset = FL_nint( offset * FL_max( 0, sp->max_height - sp->h ) );
1112 
1113     if ( ! sp->no_redraw )
1114         fl_redraw_object( obj );
1115 
1116     return fli_tbox_get_rel_yoffset( obj );
1117 }
1118 
1119 
1120 /***************************************
1121  * Returns the x-offset in pixels of the text displayed in the textbox
1122  ***************************************/
1123 
1124 int
fli_tbox_get_xoffset(FL_OBJECT * obj)1125 fli_tbox_get_xoffset( FL_OBJECT * obj )
1126 {
1127     return ( ( FLI_TBOX_SPEC * ) obj->spec )->xoffset;
1128 }
1129 
1130 
1131 /***************************************
1132  * Returns the x-offset of the text displayed in the textbox
1133  * as a number between 0 (starts of lines are shown) and 1
1134  * (end of longest line is shown)
1135  ***************************************/
1136 
1137 double
fli_tbox_get_rel_xoffset(FL_OBJECT * obj)1138 fli_tbox_get_rel_xoffset( FL_OBJECT * obj )
1139 {
1140     FLI_TBOX_SPEC *sp = obj->spec;
1141 
1142     if ( sp->max_width <= sp->w )
1143         return 0.0;
1144 
1145     return ( double ) sp->xoffset / ( sp->max_width - sp->w );
1146 }
1147 
1148 
1149 /***************************************
1150  * Returns the y-offset in pixels of the text displayed in the textbox
1151  ***************************************/
1152 
1153 int
fli_tbox_get_yoffset(FL_OBJECT * obj)1154 fli_tbox_get_yoffset( FL_OBJECT * obj )
1155 {
1156     return ( ( FLI_TBOX_SPEC * ) obj->spec )->yoffset;
1157 }
1158 
1159 
1160 /***************************************
1161  * Returns the y-offset of the text displayed in the textbox
1162  * as a number between 0 (start of text is shown) and 1 (end
1163  * of text is shown)
1164 ***************************************/
1165 
1166 double
fli_tbox_get_rel_yoffset(FL_OBJECT * obj)1167 fli_tbox_get_rel_yoffset( FL_OBJECT * obj )
1168 {
1169     FLI_TBOX_SPEC *sp = obj->spec;
1170 
1171     if ( sp->max_height <= sp->h )
1172         return 0.0;
1173 
1174     return ( double ) sp->yoffset / ( sp->max_height - sp->h );
1175 }
1176 
1177 
1178 /***************************************
1179  * Returns the y-offset in pixel for a line
1180  * (or -1 if the line doesn't exist).
1181  ***************************************/
1182 
1183 int
fli_tbox_get_line_yoffset(FL_OBJECT * obj,int line)1184 fli_tbox_get_line_yoffset( FL_OBJECT * obj,
1185                            int         line )
1186 {
1187     FLI_TBOX_SPEC *sp = obj->spec;
1188 
1189     if ( line < 0 || line >= sp->num_lines )
1190         return -1;
1191 
1192     return sp->lines[ line ]->y;
1193 }
1194 
1195 
1196 /***************************************
1197  * Makes a line the one shown at the top (as far as possible)
1198  ***************************************/
1199 
1200 void
fli_tbox_set_topline(FL_OBJECT * obj,int line)1201 fli_tbox_set_topline( FL_OBJECT * obj,
1202                       int         line )
1203 {
1204     FLI_TBOX_SPEC *sp = obj->spec;
1205 
1206     if ( ! sp->num_lines )
1207         return;
1208 
1209     if ( line < 0 )
1210         line = 0;
1211     else if ( line >= sp->num_lines )
1212         line = sp->num_lines - 1;
1213 
1214     fli_tbox_set_yoffset( obj, sp->lines[ line ]->y );
1215 }
1216 
1217 
1218 /***************************************
1219  * Makes a line the lowest shown line (as far as possible)
1220  ***************************************/
1221 
1222 void
fli_tbox_set_bottomline(FL_OBJECT * obj,int line)1223 fli_tbox_set_bottomline( FL_OBJECT * obj,
1224                          int         line )
1225 {
1226     FLI_TBOX_SPEC *sp = obj->spec;
1227 
1228     if ( ! sp->num_lines )
1229         return;
1230 
1231     if ( line < 0 )
1232         line = 0;
1233     else if ( line >= sp->num_lines )
1234         line = sp->num_lines - 1;
1235 
1236     fli_tbox_set_yoffset( obj,
1237                             sp->lines[ line ]->y
1238                           + sp->lines[ line ]->h - sp->h );
1239 }
1240 
1241 
1242 /***************************************
1243  * Shifts the content to make the indexed line show up in the center
1244  * of the browser (as far as possible)
1245  ***************************************/
1246 
1247 void
fli_tbox_set_centerline(FL_OBJECT * obj,int line)1248 fli_tbox_set_centerline( FL_OBJECT * obj,
1249                          int         line )
1250 {
1251     FLI_TBOX_SPEC *sp = obj->spec;
1252 
1253     if ( ! sp->num_lines )
1254         return;
1255 
1256     if ( line < 0 )
1257         line = 0;
1258     else if ( line >= sp->num_lines )
1259         line = sp->num_lines - 1;
1260 
1261     fli_tbox_set_yoffset( obj,
1262                             sp->lines[ line ]->y
1263                           + ( sp->lines[ line ]->h - sp->h ) / 2 );
1264 }
1265 
1266 
1267 /***************************************
1268  * Removes all selections in the browser
1269  ***************************************/
1270 
1271 void
fli_tbox_deselect(FL_OBJECT * obj)1272 fli_tbox_deselect( FL_OBJECT * obj )
1273 {
1274     FLI_TBOX_SPEC *sp = obj->spec;
1275     int i;
1276 
1277     for ( i = 0; i < sp->num_lines; i++ )
1278         sp->lines[ i ]->selected = 0;
1279 
1280     sp->select_line = -1;
1281     sp->deselect_line = -1;
1282 
1283     if ( ! sp->no_redraw )
1284         fl_redraw_object( obj );
1285 }
1286 
1287 
1288 /***************************************
1289  * Deselects a line in the browser
1290  ***************************************/
1291 
1292 void
fli_tbox_deselect_line(FL_OBJECT * obj,int line)1293 fli_tbox_deselect_line( FL_OBJECT * obj,
1294                          int         line )
1295 {
1296     FLI_TBOX_SPEC *sp = obj->spec;
1297 
1298     if ( line < 0 || line >= sp->num_lines || ! sp->lines[ line ]->selected )
1299         return;
1300 
1301     sp->lines[ line ]->selected = 0;
1302 
1303     /* Don't mark as deselected for FL_SELECT_BROWSER since otherwise it
1304        would be impossible for the user to retrieve the selection */
1305 
1306     if ( obj->type != FL_SELECT_BROWSER )
1307     {
1308         sp->deselect_line = line;
1309         sp->select_line = -1;
1310     }
1311 
1312     if ( ! sp->no_redraw )
1313         fl_redraw_object( obj );
1314 }
1315 
1316 
1317 /***************************************
1318  * Selects a line in the browser (if necessary deselecting another line)
1319  ***************************************/
1320 
1321 void
fli_tbox_select_line(FL_OBJECT * obj,int line)1322 fli_tbox_select_line( FL_OBJECT * obj,
1323                       int         line )
1324 {
1325     FLI_TBOX_SPEC *sp = obj->spec;
1326 
1327     if (    line < 0
1328          || line >= sp->num_lines
1329          || sp->lines[ line ]->selected
1330          || ! sp->lines[ line ]->selectable )
1331         return;
1332 
1333     if ( sp->select_line != -1 && obj->type != FL_MULTI_BROWSER )
1334         sp->lines[ sp->select_line ]->selected = 0;
1335 
1336     sp->lines[ line ]->selected = 1;
1337 
1338     sp->select_line = line;
1339     sp->deselect_line = -1;
1340 
1341     if ( ! sp->no_redraw )
1342         fl_redraw_object( obj );
1343 }
1344 
1345 
1346 /***************************************
1347  * Returns if a line in the browser is selected
1348  ***************************************/
1349 
1350 int
fli_tbox_is_line_selected(FL_OBJECT * obj,int line)1351 fli_tbox_is_line_selected( FL_OBJECT * obj,
1352                            int         line )
1353 {
1354     FLI_TBOX_SPEC *sp = obj->spec;
1355 
1356     return    line >= 0
1357            && line < sp->num_lines
1358            && sp->lines[ line ]->selected;
1359 }
1360 
1361 
1362 /***************************************
1363  * Sets if a line is selectable or not
1364  ***************************************/
1365 
1366 void
fli_tbox_make_line_selectable(FL_OBJECT * obj,int line,int state)1367 fli_tbox_make_line_selectable( FL_OBJECT * obj,
1368                                int         line,
1369                                int         state )
1370 {
1371     FLI_TBOX_SPEC *sp = obj->spec;
1372     TBOX_LINE *tl;
1373 
1374     if (    line < 0
1375          || line >= sp->num_lines
1376          || sp->lines[ line ]->is_separator
1377          || obj->type == FL_NORMAL_BROWSER )
1378         return;
1379 
1380     tl = sp->lines[ line ];
1381     state = state ? 1 : 0;
1382 
1383     if ( ! state )
1384     {
1385         if ( line == sp->select_line )
1386             sp->select_line = -1;
1387         if ( line == sp->deselect_line )
1388             sp->deselect_line = -1;
1389     }
1390 
1391     if ( tl->selectable != state )
1392     {
1393         tl->selectable = state;
1394 
1395         if ( tl->is_special )
1396         {
1397             if ( tl->specialGC )
1398             {
1399                 XFreeGC( flx->display, tl->specialGC );
1400                 sp->lines[ line ]->specialGC = None;
1401             }
1402 
1403             if ( FL_ObjWin( obj ) )
1404                 tl->specialGC = create_gc( obj, tl->style, tl->size,
1405                                            state ? obj->lcol : FL_INACTIVE,
1406                                            sp->x, sp->y, sp->w, sp->h );
1407         }
1408     }
1409 
1410     if ( ! sp->no_redraw )
1411         fl_redraw_object( obj );
1412 }
1413 
1414 
1415 /***************************************
1416  * Returns the last selected or deselected line (or 0 if there's none).
1417  * Please note: this function returns the index of the selected line
1418  * incremented by one and the negative of the index of the deselected
1419  * line decremented by 1.
1420  ***************************************/
1421 
1422 int
fli_tbox_get_selection(FL_OBJECT * obj)1423 fli_tbox_get_selection( FL_OBJECT *obj )
1424 {
1425     FLI_TBOX_SPEC *sp = obj->spec;
1426 
1427     if ( sp->select_line >= 0 )
1428         return sp->select_line + 1;
1429     else if ( sp->deselect_line >= 0 )
1430         return - sp->deselect_line - 1;
1431     else
1432         return 0;
1433 }
1434 
1435 
1436 /***************************************
1437  * Installs a handler for double and triple clicks
1438  ***************************************/
1439 
1440 void
fli_tbox_set_dblclick_callback(FL_OBJECT * obj,FL_CALLBACKPTR cb,long data)1441 fli_tbox_set_dblclick_callback( FL_OBJECT      * obj,
1442                                 FL_CALLBACKPTR   cb,
1443                                 long             data )
1444 {
1445     FLI_TBOX_SPEC *sp = obj->spec;
1446 
1447     sp->callback = cb;
1448     sp->callback_data = data;
1449     fl_set_object_dblclick( obj, cb ? FL_CLICK_TIMEOUT : 0 );
1450 }
1451 
1452 
1453 /***************************************
1454  * Creates a GC with the required settings
1455  ***************************************/
1456 
1457 static GC
create_gc(FL_OBJECT * obj,int style,int size,FL_COLOR color,int clip_x,int clip_y,int clip_w,int clip_h)1458 create_gc( FL_OBJECT * obj,
1459            int         style,
1460            int         size,
1461            FL_COLOR    color,
1462            int         clip_x,
1463            int         clip_y,
1464            int         clip_w,
1465            int         clip_h )
1466 {
1467     GC gc;
1468     XGCValues xgcv;
1469     unsigned long gcvm;
1470 
1471     if ( fli_cntl.safe )
1472         xgcv.graphics_exposures = 1;
1473     else
1474     {
1475         Screen *scr = ScreenOfDisplay( flx->display, fl_screen );
1476 
1477         xgcv.graphics_exposures =    ! DoesBackingStore( scr )
1478                                   || ! fli_cntl.backingStore;
1479     }
1480 
1481     gcvm = GCGraphicsExposures | GCForeground;
1482 
1483     xgcv.foreground = fl_get_flcolor( color );
1484     gc = XCreateGC( flx->display, FL_ObjWin( obj ), gcvm, &xgcv );
1485 
1486     if ( size > 0 && style >= 0 )
1487     {
1488         XFontStruct *xfs = fl_get_fntstruct( style, size );
1489 
1490         XSetFont( flx->display, gc, xfs->fid );
1491     }
1492 
1493     fl_set_gc_clipping( gc, obj->x + clip_x, obj->y + clip_y, clip_w, clip_h );
1494 
1495     return gc;
1496 }
1497 
1498 
1499 /***************************************
1500  ***************************************/
1501 
1502 void
fli_tbox_recalc_area(FL_OBJECT * obj)1503 fli_tbox_recalc_area( FL_OBJECT * obj )
1504 {
1505     FLI_TBOX_SPEC *sp = obj->spec;
1506     int dummy;
1507 
1508     sp->x = FL_abs( obj->bw ) + LEFT_MARGIN;
1509     sp->y = FL_abs( obj->bw ) + TOP_MARGIN;
1510     sp->w = obj->w - 2 * FL_abs( obj->bw ) - LEFT_MARGIN - RIGHT_MARGIN;
1511     sp->h = obj->h - 2 * FL_abs( obj->bw ) - TOP_MARGIN - BOTTOM_MARGIN;
1512 
1513     /* This is necessary because different box types don't have all the same
1514        inside size - but it will look still wrong with anything but up and
1515        down boxes... */
1516 
1517     if ( obj->boxtype == FL_UP_BOX )
1518     {
1519         sp->x++;
1520         sp->y++;
1521         sp->w -= 2;
1522         sp->h -= 2;
1523     }
1524 
1525     /* Calculate height of line with default font */
1526 
1527     sp->def_height = fl_get_string_height( sp->def_style, sp->def_size,
1528                                            "X", 1, &dummy, &dummy );
1529 }
1530 
1531 
1532 /***************************************
1533  * Function called whenever the size or some other visual attribute
1534  * was changed before redrawing the object
1535  ***************************************/
1536 
1537 static void
fli_tbox_prepare_drawing(FL_OBJECT * obj)1538 fli_tbox_prepare_drawing( FL_OBJECT * obj )
1539 {
1540     FLI_TBOX_SPEC *sp = obj->spec;
1541     int i;
1542     double old_xrel = fli_tbox_get_rel_xoffset( obj );
1543     double old_yrel = fli_tbox_get_rel_yoffset( obj );
1544     int old_no_redraw = sp->no_redraw;
1545 
1546     fli_tbox_recalc_area( obj );
1547 
1548     /* Recalculate horizontal positions of all lines */
1549 
1550     for ( i = 0; i < sp->num_lines; i++ )
1551         if ( fl_is_center_lalign( sp->lines[ i ]->align ) )
1552             sp->lines[ i ]->x = ( sp->max_width - sp->lines[ i ]->w ) / 2;
1553         else if ( fl_to_outside_lalign( sp->lines[ i ]->align ) ==
1554                                                           FL_ALIGN_RIGHT )
1555             sp->lines[ i ]->x = sp->max_width - sp->lines[ i ]->w;
1556 
1557     /* We might get called before the textbox is shown and then the
1558        window is still unknown and GCs can't be created */
1559 
1560     if ( ! FL_ObjWin( obj ) )
1561         return;
1562 
1563     /* Create default GC for text drawing */
1564 
1565     if ( sp->defaultGC )
1566         XFreeGC( flx->display, sp->defaultGC );
1567 
1568     sp->defaultGC = create_gc( obj, sp->def_style, sp->def_size, obj->lcol,
1569                                sp->x, sp->y, sp->w, sp->h );
1570 
1571     /* Create background GC for redraw deselected lines */
1572 
1573     if ( sp->backgroundGC )
1574         XFreeGC( flx->display, sp->backgroundGC );
1575 
1576     sp->backgroundGC = create_gc( obj, -1, 0, obj->col1,
1577                                   sp->x - ( LEFT_MARGIN > 0 ),
1578                                   sp->y, sp->w + ( LEFT_MARGIN > 0 ), sp->h );
1579 
1580     /* Create select GC for marking selected lines */
1581 
1582     if ( sp->selectGC )
1583         XFreeGC( flx->display, sp->selectGC );
1584 
1585     sp->selectGC = create_gc( obj, -1, 0,
1586                               fli_dithered( fl_vmode ) ?
1587                               FL_BLACK : obj->col2,
1588                               sp->x - ( LEFT_MARGIN > 0 ), sp->y,
1589                               sp->w + ( LEFT_MARGIN > 0 ), sp->h );
1590 
1591     /* Create GC for text of non-selectable lines */
1592 
1593     if ( sp->nonselectGC )
1594         XFreeGC( flx->display, sp->nonselectGC );
1595 
1596     sp->nonselectGC = create_gc( obj, sp->def_style, sp->def_size, FL_INACTIVE,
1597                                  sp->x, sp->y, sp->w, sp->h );
1598 
1599     /* Special GC for text of selected lines in B&W */
1600 
1601     if ( fli_dithered( fl_vmode ) )
1602     {
1603         if ( sp->bw_selectGC )
1604             XFreeGC( flx->display, sp->bw_selectGC );
1605 
1606         sp->bw_selectGC = create_gc( obj, sp->def_style, sp->def_size, FL_WHITE,
1607                                      sp->x - ( LEFT_MARGIN > 0 ), sp->y,
1608                                      sp->w + ( LEFT_MARGIN > 0 ), sp->h );
1609     }
1610 
1611     /* Lines with non-default fonts or colors have their own GCs */
1612 
1613     for ( i = 0; i < sp->num_lines;  i++ )
1614     {
1615         TBOX_LINE *tl = sp->lines[ i ];
1616 
1617         if ( ! tl->is_special )
1618             continue;
1619 
1620         if ( tl->specialGC )
1621         {
1622             XFreeGC( flx->display, tl->specialGC );
1623             tl->specialGC = None;
1624         }
1625 
1626         tl->specialGC = create_gc( obj, tl->style, tl->size, tl->color,
1627                                    sp->x, sp->y, sp->w, sp->h );
1628     }
1629 
1630     sp->no_redraw = 1;
1631     fli_tbox_set_rel_xoffset( obj, old_xrel );
1632     fli_tbox_set_rel_yoffset( obj, old_yrel );
1633     sp->no_redraw = old_no_redraw;
1634 }
1635 
1636 
1637 /***************************************
1638  * Frees all resources needed for the FLI_TBOX_SPEC structure
1639  ***************************************/
1640 
1641 static void
free_tbox_spec(FL_OBJECT * obj)1642 free_tbox_spec( FL_OBJECT * obj )
1643 {
1644     FLI_TBOX_SPEC *sp = obj->spec;
1645     int i;
1646 
1647     for ( i = 0; i < sp->num_lines; i++ )
1648     {
1649         if ( sp->lines[ i ]->specialGC )
1650             XFreeGC( flx->display, sp->lines[ i ]->specialGC );
1651 
1652         fli_safe_free( sp->lines[ i ]->fulltext );
1653         fli_safe_free( sp->lines[ i ] );
1654     }
1655 
1656     fli_safe_free( sp->lines );
1657 
1658     if ( sp->defaultGC )
1659         XFreeGC( flx->display, sp->defaultGC );
1660 
1661     if ( sp->backgroundGC )
1662         XFreeGC( flx->display, sp->backgroundGC );
1663 
1664     if ( sp->selectGC )
1665         XFreeGC( flx->display, sp->selectGC );
1666 
1667     if ( sp->nonselectGC )
1668         XFreeGC( flx->display, sp->nonselectGC );
1669 
1670     if ( sp->bw_selectGC )
1671         XFreeGC( flx->display, sp->bw_selectGC );
1672 
1673     fli_safe_free( obj->spec );
1674 }
1675 
1676 
1677 /***************************************
1678  * Draws the complete textbox
1679  ***************************************/
1680 
1681 static void
draw_tbox(FL_OBJECT * obj)1682 draw_tbox( FL_OBJECT * obj )
1683 {
1684     FLI_TBOX_SPEC *sp = obj->spec;
1685     int i;
1686 
1687     fl_draw_box( obj->boxtype, obj->x, obj->y, obj->w, obj->h,
1688                  obj->col1, obj->bw );
1689 
1690     XFillRectangle( flx->display, FL_ObjWin( obj ),
1691                     sp->backgroundGC,
1692                     obj->x + sp->x - ( LEFT_MARGIN > 0 ),
1693                     obj->y + sp->y + sp->w - sp->yoffset,
1694                     sp->w + ( LEFT_MARGIN > 0 ), sp->h );
1695 
1696     if ( sp->num_lines == 0 )
1697         return;
1698 
1699     fl_set_clipping( obj->x, obj->y, obj->w, obj->h );
1700 
1701     for ( i = 0; i < sp->num_lines; i++ )
1702     {
1703         TBOX_LINE *tl;
1704         GC activeGC = sp->defaultGC;
1705 
1706         tl = sp->lines[ i ];
1707 
1708         if ( tl->y + tl->h < sp->yoffset )   /* if line is above tbox */
1709             continue;
1710 
1711         if ( tl->y >= sp->h + sp->yoffset )  /* if line is below tbox */
1712             break;
1713 
1714         /* Separator lines obviously need to be treated differently from
1715            normal text */
1716 
1717         if ( tl->is_separator )
1718         {
1719             /* The extra horizontal pixels here are due to the function called
1720                subtracting them! */
1721 
1722             fl_draw_text( 0, obj->x + sp->x - 3,
1723                           obj->y + sp->y - sp->yoffset + tl->y + tl->h / 2,
1724                           sp->w + 6, 1,
1725                           FL_COL1, FL_NORMAL_STYLE, sp->def_size, "@DnLine" );
1726             continue;
1727         }
1728 
1729         /* Draw background of line in selection color if necessary*/
1730 
1731         if ( tl->selected )
1732             XFillRectangle( flx->display, FL_ObjWin( obj ), sp->selectGC,
1733                             obj->x + sp->x - ( LEFT_MARGIN > 0 ),
1734                             obj->y + sp->y + tl->y - sp->yoffset,
1735                             sp->w + ( LEFT_MARGIN > 0 ), tl->h );
1736 
1737 
1738         /* If there's no text or the text isn't visible within the textbox
1739            nothing needs to be drawn */
1740 
1741         if (    ! *tl->text
1742              || tl->x - sp->xoffset >= sp->w
1743              || tl->x + tl->w - sp->xoffset < 0 )
1744             continue;
1745 
1746         /* If the line needs a different font or color than the default use
1747            a special GC just for that line */
1748 
1749         if ( ! tl->selectable )
1750             activeGC = sp->nonselectGC;
1751 
1752         if ( tl->is_special )
1753         {
1754             if ( ! tl->specialGC )
1755                 tl->specialGC = create_gc( obj, tl->style, tl->size,
1756                                            tl->selectable ?
1757                                            tl->color : FL_INACTIVE,
1758                                            sp->x, sp->y, sp->w, sp->h );
1759 
1760             activeGC = tl->specialGC;
1761         }
1762 
1763         /* Set up GC for selected lines in B&W each time round - a bit slow,
1764            but I guess there are hardly any machines left with a B&W display */
1765 
1766         if ( fli_dithered( fl_vmode ) && tl->selected )
1767         {
1768             XFontStruct *xfs = fl_get_fntstruct( tl->style, tl->size );
1769 
1770             XSetFont( flx->display, sp->bw_selectGC, xfs->fid );
1771             XSetForeground( flx->display, sp->bw_selectGC,
1772                             fl_get_flcolor( FL_WHITE ) );
1773             activeGC = sp->bw_selectGC;
1774         }
1775 
1776         /* Now draw the line, underlined if necessary */
1777 
1778         if ( tl->is_underlined )
1779             fl_diagline( obj->x + sp->x - sp->xoffset + tl->x,
1780                          obj->y + sp->y - sp->yoffset + tl->y + tl->h - 1,
1781                          FL_min( sp->w + sp->xoffset - tl->x, tl->w ), 1,
1782                          ( fli_dithered( fl_vmode ) && tl->selected ) ?
1783                          FL_WHITE : tl->color );
1784 
1785         fli_draw_stringTAB( FL_ObjWin( obj ), activeGC,
1786                             obj->x + sp->x - sp->xoffset + tl->x,
1787                             obj->y + sp->y - sp->yoffset + tl->y + tl->asc,
1788                             tl->style, tl->size, tl->text, tl->len, 0 );
1789     }
1790 
1791     fl_unset_clipping( );
1792 }
1793 
1794 
1795 /***************************************
1796  * Tries to find the index of the next selectable line following
1797  * 'line', returns the total number of lines if none can be found.
1798  ***************************************/
1799 
1800 static int
find_next_selectable(FL_OBJECT * obj,int line)1801 find_next_selectable( FL_OBJECT * obj,
1802                       int         line )
1803 {
1804     FLI_TBOX_SPEC *sp = obj->spec;
1805 
1806     if ( line < -1 || line >= sp->num_lines )
1807         line = -1;
1808 
1809     while ( ++line < sp->num_lines )
1810         if ( sp->lines[ line ]->selectable )
1811             break;
1812 
1813     return line < sp->num_lines ? line : -1;
1814 }
1815 
1816 
1817 /***************************************
1818  * Tries to find the index of the next selectable line
1819  * before 'line', returns -1 if none can be found.
1820  ***************************************/
1821 
1822 static int
find_previous_selectable(FL_OBJECT * obj,int line)1823 find_previous_selectable( FL_OBJECT * obj,
1824                           int         line )
1825 {
1826     FLI_TBOX_SPEC *sp = obj->spec;
1827 
1828     if ( line < 0 || line > sp->num_lines )
1829         line = sp->num_lines;
1830 
1831     while ( --line >= 0 )
1832         if ( sp->lines[ line ]->selectable )
1833             break;
1834 
1835     return line;
1836 }
1837 
1838 
1839 /***************************************
1840  * Returns the total number of lines in the browser
1841  ***************************************/
1842 
1843 int
fli_tbox_get_num_lines(FL_OBJECT * obj)1844 fli_tbox_get_num_lines( FL_OBJECT * obj )
1845 {
1846     FLI_TBOX_SPEC *sp = obj->spec;
1847 
1848     return sp->num_lines;
1849 }
1850 
1851 
1852 /***************************************
1853  * Returns the index of the first line that is completete shown on the
1854  * screen or -1 if there are no lines
1855  ***************************************/
1856 
1857 int
fli_tbox_get_topline(FL_OBJECT * obj)1858 fli_tbox_get_topline( FL_OBJECT * obj )
1859 {
1860     FLI_TBOX_SPEC *sp = obj->spec;
1861     int i;
1862 
1863     if ( ! sp->num_lines )
1864         return -1;
1865 
1866     /* If the box was never shown assume that the very first line will
1867        be the one that's going to be in topmost position */
1868 
1869     if ( ! sp->def_height )
1870         return 0;
1871 
1872     i = FL_min( sp->yoffset / sp->def_height, sp->num_lines - 1 );
1873 
1874     if ( sp->lines[ i ]->y < sp->yoffset )
1875     {
1876         while ( ++i < sp->num_lines && sp->lines[ i ]->y < sp->yoffset )
1877             /* empty */ ;
1878         if ( i == sp->num_lines || sp->lines[ i ]->y > sp->yoffset + sp->h )
1879             i--;
1880     }
1881     else if ( sp->lines[ i ]->y > sp->yoffset )
1882     {
1883         while ( i-- > 0 && sp->lines[ i ]->y > sp->yoffset )
1884             /* empty */ ;
1885         if ( i < 0 || sp->lines[ i ]->y < sp->yoffset )
1886             i++;
1887     }
1888 
1889     return i < sp->num_lines ? i : -1;
1890 }
1891 
1892 
1893 /***************************************
1894  * Returns the index of the last line that is completete shown on the
1895  * screen or -1 if there are no lines
1896  ***************************************/
1897 
1898 int
fli_tbox_get_bottomline(FL_OBJECT * obj)1899 fli_tbox_get_bottomline( FL_OBJECT * obj )
1900 {
1901     FLI_TBOX_SPEC *sp = obj->spec;
1902     int i = sp->num_lines;
1903 
1904     while ( --i >= 0
1905             && sp->lines[ i ]->y > sp->yoffset
1906             && sp->lines[ i ]->y + sp->lines[ i ]->h > sp->yoffset + sp->h )
1907         /* empty */ ;
1908 
1909     return i;
1910 }
1911 
1912 
1913 /***************************************
1914  * Deals with keyboard input (and indirectly with mouse wheel "clicks")
1915  ***************************************/
1916 
1917 static int
handle_keyboard(FL_OBJECT * obj,int key)1918 handle_keyboard( FL_OBJECT * obj,
1919                  int         key )
1920 {
1921     FLI_TBOX_SPEC *sp = obj->spec;
1922     int old_select_line = sp->select_line;
1923     int old_yoffset = sp->yoffset;
1924     int old_xoffset = sp->xoffset;
1925     int ret = FL_RETURN_NONE;
1926 
1927     /* Don't react to keyboard events while deactivated, the browser also
1928        doesn't react to the mouse, so anything else woild seen to be
1929        inconsistent */
1930 
1931     if ( ! obj->active )
1932         return ret;
1933 
1934     if ( IsHome( key ) && sp->react_to_vert )
1935         fli_tbox_set_rel_yoffset( obj, 0.0 );
1936     else if ( IsEnd( key ) && sp->react_to_vert )
1937         fli_tbox_set_rel_yoffset( obj, 1.0 );
1938     else if ( IsPageUp( key ) && sp->react_to_vert )
1939         fli_tbox_set_yoffset( obj, sp->yoffset - sp->h );
1940     else if ( IsHalfPageUp( key ) && sp->react_to_vert )
1941         fli_tbox_set_yoffset( obj, sp->yoffset - sp->h / 2 );
1942     else if ( Is1LineUp( key ) && sp->react_to_vert )
1943         fli_tbox_set_yoffset( obj, sp->yoffset - sp->def_height );
1944     else if ( ( IsPageDown( key ) || key == ' ' ) && sp->react_to_vert )
1945         fli_tbox_set_yoffset( obj, sp->yoffset + sp->h );
1946     else if ( IsHalfPageDown( key ) && sp->react_to_vert )
1947         fli_tbox_set_yoffset( obj, sp->yoffset + sp->h / 2 );
1948     else if ( Is1LineDown( key ) && sp->react_to_vert )
1949         fli_tbox_set_yoffset( obj, sp->yoffset + sp->def_height );
1950     else if ( IsLeft( key ) && sp->react_to_hori )
1951         fli_tbox_set_xoffset( obj, sp->xoffset - 3 );
1952     else if ( IsRight( key ) && sp->react_to_hori )
1953         fli_tbox_set_xoffset( obj, sp->xoffset + 3 );
1954     else if ( IsUp( key ) )
1955     {
1956         if (    sp->react_to_vert
1957              && (    obj->type == FL_NORMAL_BROWSER
1958                   || obj->type == FL_SELECT_BROWSER
1959                   || obj->type == FL_MULTI_BROWSER ) )
1960         {
1961             int topline = fli_tbox_get_topline( obj );
1962 
1963             if ( --topline >= 0 )
1964                 fli_tbox_set_yoffset( obj, sp->lines[ topline ]->y );
1965         }
1966         else if (    obj->type == FL_HOLD_BROWSER
1967                   || obj->type == FL_DESELECTABLE_HOLD_BROWSER )
1968         {
1969             TBOX_LINE *tl;
1970             int line = find_previous_selectable( obj, sp->select_line );
1971 
1972             if ( line >= 0 )
1973             {
1974                 tl = sp->lines[ line ];
1975 
1976                 if ( sp->react_to_vert
1977                      || ( tl->y + tl->h >= sp->yoffset
1978                           && tl->y < sp->h + sp->yoffset ) )
1979                 {
1980                     fli_tbox_select_line( obj, line );
1981 
1982                     tl = sp->lines[ sp->select_line ];
1983 
1984                     /* Bring the selection into view if necessary */
1985 
1986                     if ( tl->y < sp->yoffset )
1987                         fli_tbox_set_topline( obj, sp->select_line );
1988                     else if ( tl->y + tl->h - sp->yoffset >= sp->h )
1989                         fli_tbox_set_bottomline( obj, sp->select_line );
1990                 }
1991             }
1992         }
1993     }
1994     else if ( IsDown( key ) )
1995     {
1996         if (    sp->react_to_vert
1997              && (    obj->type == FL_NORMAL_BROWSER
1998                   || obj->type == FL_SELECT_BROWSER
1999                   || obj->type == FL_MULTI_BROWSER ) )
2000         {
2001             int topline = fli_tbox_get_topline( obj );
2002 
2003             if ( topline >= 0 && topline < sp->num_lines - 1 )
2004             {
2005                 if ( sp->lines[ topline ]->y - sp->yoffset == 0 )
2006                     topline++;
2007 
2008                 fli_tbox_set_yoffset( obj, sp->lines[ topline ]->y );
2009             }
2010             else
2011                 fli_tbox_set_yoffset( obj, sp->max_height );
2012         }
2013         else if (    obj->type == FL_HOLD_BROWSER
2014                   || obj->type == FL_DESELECTABLE_HOLD_BROWSER )
2015         {
2016             TBOX_LINE *tl;
2017             int line = find_next_selectable( obj, sp->select_line );
2018 
2019             if ( line >= 0 )
2020             {
2021                 tl = sp->lines[ line ];
2022 
2023                 if ( sp->react_to_vert
2024                      || ( tl->y + tl->h >= sp->yoffset
2025                           && tl->y < sp->h + sp->yoffset ) )
2026                 {
2027                     fli_tbox_select_line( obj, line );
2028 
2029                     tl = sp->lines[ sp->select_line ];
2030 
2031                     /* Bring the selection into view if necessary */
2032 
2033                     if ( tl->y + tl->h < sp->yoffset )
2034                         fli_tbox_set_topline( obj, sp->select_line );
2035                     else if ( tl->y + tl->h - sp->yoffset >= sp->h )
2036                         fli_tbox_set_bottomline( obj, sp->select_line );
2037                 }
2038             }
2039         }
2040     }
2041 
2042     if ( old_select_line != sp->select_line )
2043         ret |= FL_RETURN_SELECTION;
2044     if ( old_yoffset != sp->yoffset || old_xoffset != sp->xoffset )
2045         ret |= FL_RETURN_CHANGED | FL_RETURN_END;
2046 
2047     return ret;
2048 }
2049 
2050 
2051 /***************************************
2052  * Tries to find the index of the line under the mouse,
2053  * returns -1 if there's none
2054  ***************************************/
2055 
2056 static int
find_mouse_line(FL_OBJECT * obj,FL_Coord my)2057 find_mouse_line( FL_OBJECT * obj,
2058                  FL_Coord    my )
2059 {
2060     FLI_TBOX_SPEC *sp = obj->spec;
2061     int line;
2062 
2063     if ( my < obj->y + sp->y || my > obj->y + sp->y + sp->h )
2064         return -1;
2065 
2066     my += sp->yoffset - sp->y - obj->y;
2067 
2068     line = FL_min( sp->num_lines - 1,
2069                    obj->y / ( ( double ) sp->max_height / sp->num_lines ) );
2070 
2071     if ( sp->lines[ line ]->y > my )
2072         while ( line-- > 0
2073                 && sp->lines[ line ]->y > my )
2074             /* empty */ ;
2075     else
2076         while ( sp->lines[ line ]->y + sp->lines[ line ]->h < my
2077                 && ++line < sp->num_lines )
2078             /* empty */ ;
2079 
2080     if ( line < 0 || line >= sp->num_lines )
2081         return -1;
2082 
2083     return line;
2084 }
2085 
2086 
2087 /***************************************
2088  * Sets if the textbox reacts to keys that change the vertical
2089  * position (used by browser when vertical scrollbar is switched
2090  * on or off)
2091  ***************************************/
2092 
2093 void
fli_tbox_react_to_vert(FL_OBJECT * obj,int state)2094 fli_tbox_react_to_vert( FL_OBJECT * obj,
2095                         int         state )
2096 {
2097     FLI_TBOX_SPEC *sp = obj->spec;
2098 
2099     sp->react_to_vert = state ? 1 : 0;
2100 }
2101 
2102 
2103 /***************************************
2104  * Sets if the textbox reacts to keys that change the horizontal
2105  * position (used by browser when horizontal scrollbar is switched
2106  * on or off)
2107  ***************************************/
2108 
2109 void
fli_tbox_react_to_hori(FL_OBJECT * obj,int state)2110 fli_tbox_react_to_hori( FL_OBJECT * obj,
2111                         int         state )
2112 {
2113     FLI_TBOX_SPEC *sp = obj->spec;
2114 
2115     sp->react_to_hori = state ? 1 : 0;
2116 }
2117 
2118 
2119 /***************************************
2120  * Handles a mouse event, returns whether a selection change has occured
2121  ***************************************/
2122 
2123 #define DESELECT 0
2124 #define SELECT   1
2125 
2126 static int
handle_mouse(FL_OBJECT * obj,FL_Coord my,int ev)2127 handle_mouse( FL_OBJECT * obj,
2128               FL_Coord    my,
2129               int         ev )
2130 {
2131     FLI_TBOX_SPEC *sp = obj->spec;
2132     int line;
2133     int ret = FL_RETURN_NONE;
2134     static int mode;
2135     static int last_multi = -1;
2136 
2137     /* Check whether there are any lines at all */
2138 
2139     if ( sp->num_lines == 0 )
2140         return ret;
2141 
2142     /* Figure out the index of the line the mouse is on. If the mouse is below
2143        or above the text area scroll up or down */
2144 
2145     if (    ev == FL_UPDATE
2146          && sp->react_to_vert
2147          && (    my < obj->y + sp->y
2148               || my > obj->y + sp->y + sp-> h ) )
2149     {
2150         if ( my < obj->y + sp->y )
2151         {
2152             line = fli_tbox_get_topline( obj );
2153             if ( line > 0 )
2154             {
2155                 fli_tbox_set_topline( obj, --line );
2156                 ret |= FL_RETURN_CHANGED;
2157             }
2158         }
2159         else
2160         {
2161             line = fli_tbox_get_bottomline( obj );
2162             if ( line > 0 && line < sp->num_lines - 1 )
2163             {
2164                 fli_tbox_set_bottomline( obj, ++line );
2165                 ret |= FL_RETURN_CHANGED;
2166             }
2167         }
2168     }
2169     else if ( obj->type != FL_NORMAL_BROWSER )
2170         line = find_mouse_line( obj, my );
2171 
2172     /* A normal textbox doesn't react to the mouse in any other ways */
2173 
2174     if ( obj->type == FL_NORMAL_BROWSER )
2175         return ret;
2176     else if (    obj->type == FL_SELECT_BROWSER
2177               || obj->type == FL_HOLD_BROWSER
2178               || obj->type == FL_DESELECTABLE_HOLD_BROWSER )
2179     {
2180         /* For FL_SELECT_BROWSER browsers the selection is undone when the
2181            mouse is released */
2182 
2183         if ( ev == FL_RELEASE && obj->type == FL_SELECT_BROWSER )
2184         {
2185             if ( sp->select_line >= 0 )
2186                 fli_tbox_deselect_line( obj, sp->select_line );
2187             return ret;
2188         }
2189 
2190         if ( line < 0 || ! sp->lines[ line ]->selectable )
2191             return ret;
2192 
2193         if ( ev == FL_PUSH )
2194         {
2195 #if 0  /* still under discussion with Serge Bromow */
2196             if ( line != sp->select_line )
2197             {
2198                 fli_tbox_select_line( obj, line );
2199                 ret |= FL_RETURN_SELECTION;
2200             }
2201             else  if (    line == sp->select_line
2202                        && obj->type == FL_DESELECTABLE_HOLD_BROWSER )
2203             {
2204                 fli_tbox_deselect_line( obj, line );
2205                 ret |= FL_RETURN_DESELECTION;
2206             }
2207 #endif
2208             if (    line == sp->select_line
2209                  && obj->type == FL_DESELECTABLE_HOLD_BROWSER )
2210             {
2211                 fli_tbox_deselect_line( obj, line );
2212                 ret |= FL_RETURN_DESELECTION;
2213             }
2214             else
2215             {
2216                 fli_tbox_select_line( obj, line );
2217                 ret |= FL_RETURN_SELECTION;
2218             }
2219         }
2220 
2221         return ret;
2222     }
2223     else  /* FL_MULTI_BROWSER */
2224     {
2225         if ( line < 0 )
2226             return ret;
2227 
2228         if ( ev == FL_PUSH )
2229         {
2230             if ( ! sp->lines[ line ]->selectable )
2231                 return ret;
2232 
2233             mode = sp->lines[ line ]->selected ? DESELECT : SELECT;
2234 
2235             if ( mode == SELECT )
2236             {
2237                 fli_tbox_select_line( obj, line );
2238                 last_multi = line;
2239                 ret |= FL_RETURN_SELECTION;
2240             }
2241             else
2242             {
2243                 fli_tbox_deselect_line( obj, line );
2244                 last_multi = line;
2245                 ret |= FL_RETURN_DESELECTION;
2246             }
2247         }
2248         else
2249         {
2250             /* Mouse may have been moved that fast that one or more lines
2251                got skipped */
2252 
2253             if ( last_multi != -1 && FL_abs( line - last_multi ) > 1 )
2254             {
2255                 int incr = line - last_multi > 1 ? 1 : -1;
2256 
2257                 while ( ( last_multi += incr ) != line )
2258                     if ( sp->lines[ last_multi ]->selectable )
2259                     {
2260                         if (    mode == SELECT
2261                              && ! sp->lines[ last_multi ]->selected )
2262                         {
2263                             fli_tbox_select_line( obj, last_multi );
2264                             ret |= FL_RETURN_SELECTION;
2265                         }
2266                         else if (    mode == DESELECT
2267                                   && sp->lines[ last_multi ]->selected )
2268                         {
2269                             fli_tbox_deselect_line( obj, last_multi );
2270                             ret |= FL_RETURN_DESELECTION;
2271                         }
2272                     }
2273             }
2274 
2275             if ( sp->lines[ line ]->selectable )
2276             {
2277                 if (    mode == SELECT
2278                      && ! sp->lines[ line ]->selected )
2279                 {
2280                     fli_tbox_select_line( obj, line );
2281                     last_multi = line;
2282                     ret |= FL_RETURN_SELECTION;
2283                 }
2284                 else if (    mode == DESELECT
2285                           && sp->lines[ line ]->selected )
2286                 {
2287                     fli_tbox_deselect_line( obj, line );
2288                     ret |= FL_RETURN_DESELECTION;
2289                     last_multi = line;
2290                 }
2291             }
2292 
2293             if ( ev == FL_RELEASE )
2294                 last_multi = -1;
2295         }
2296     }
2297 
2298     return ret;
2299 }
2300 
2301 
2302 /***************************************
2303  * Called for events concerning the textbox
2304  ***************************************/
2305 
2306 static int
handle_tbox(FL_OBJECT * obj,int ev,FL_Coord mx FL_UNUSED_ARG,FL_Coord my,int key,void * xev)2307 handle_tbox( FL_OBJECT * obj,
2308              int         ev,
2309              FL_Coord    mx  FL_UNUSED_ARG,
2310              FL_Coord    my,
2311              int         key,
2312              void      * xev )
2313 {
2314     FLI_TBOX_SPEC *sp = obj->spec;
2315     int ret = FL_RETURN_NONE;
2316     static int old_yoffset = -1;
2317 
2318     if (     obj->type == FL_NORMAL_BROWSER
2319           && key == FL_MBUTTON1
2320           && sp->select_line >= 0 )
2321 		fli_tbox_deselect_line( obj, sp->select_line );
2322 
2323     /* Convert mouse wheel events to keypress events */
2324 
2325     if (    ev == FL_RELEASE
2326          && ( key == FL_MBUTTON4 || key == FL_MBUTTON5 )
2327          && ! obj->want_update
2328          && ! fli_mouse_wheel_to_keypress( &ev, &key, xev ) )
2329         return ret;
2330 
2331     switch ( ev )
2332     {
2333         case FL_ATTRIB :
2334         case FL_RESIZED :
2335             sp->attrib = 1;
2336             break;
2337 
2338         case FL_DRAW :
2339             if ( sp->attrib )
2340             {
2341                 fli_tbox_prepare_drawing( obj );
2342                 sp->attrib = 0;
2343             }
2344 
2345             draw_tbox( obj );
2346             break;
2347 
2348         case FL_DBLCLICK :
2349         case FL_TRPLCLICK :
2350             if ( sp->callback )
2351                 sp->callback( obj, sp->callback_data );
2352             break;
2353 
2354         case FL_KEYPRESS :
2355             ret = handle_keyboard( obj, key );
2356             break;
2357 
2358         case FL_PUSH :
2359             if ( key != FL_MBUTTON1 )
2360                 break;
2361             obj->want_update = 1;       /* so we can follow mouse movements */
2362             old_yoffset = sp->yoffset;
2363             ret = handle_mouse( obj, my, ev );
2364             break;
2365 
2366         case FL_UPDATE :
2367             ret = handle_mouse( obj, my, ev );
2368             break;
2369 
2370         case FL_RELEASE :
2371             if ( key != FL_MBUTTON1 )
2372                 break;
2373             ret = handle_mouse( obj, my, ev ) | FL_RETURN_END;
2374             if ( sp->yoffset != old_yoffset )
2375                 ret |= FL_RETURN_CHANGED;
2376             obj->want_update = 0;
2377             break;
2378 
2379         case FL_FREEMEM :
2380             free_tbox_spec( obj );
2381             break;
2382     }
2383 
2384     return ret;
2385 }
2386 
2387 
2388 /*
2389  * Local variables:
2390  * tab-width: 4
2391  * indent-tabs-mode: nil
2392  * End:
2393  */
2394