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