1 /*
2  *  This file is part of the XForms library package.
3  *
4  *  XForms is free software; you can redistribute it and/or modify it
5  *  under the terms of the GNU Lesser General Public License as
6  *  published by the Free Software Foundation; either version 2.1, or
7  *  (at your option) any later version.
8  *
9  *  XForms is distributed in the hope that it will be useful, but
10  *  WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public License
15  *  along with XForms.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 
19 /**
20  * \file input.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao and Mark Overmars
24  *  All rights reserved.
25  *
26  *  XForms Class FL_INPUT
27  *     handle normal user inputs and exchange data with other
28  *     applications via the X Selection machnism.
29  *
30  *  Data structure is grossly wrong and very inefficient.
31  *  Need to complete overhaul this someday.
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37 
38 #include "include/forms.h"
39 #include "flinternal.h"
40 #include <string.h>
41 #include <sys/types.h>
42 #include <stdlib.h>
43 #include <ctype.h>
44 #include "private/pinput.h"
45 #include "private/flvasprintf.h"
46 
47 
48 enum {
49     COMPLETE = 0,
50     PARTIAL  = 1,
51     VSLIDER  = 2,
52     HSLIDER  = 4
53 };
54 
55 
56 static void correct_topline( FLI_INPUT_SPEC *,
57                              int * );
58 
59 static void redraw_scrollbar( FL_OBJECT * );
60 
61 static int make_line_visible( FL_OBJECT * obj,
62                               int         ypos );
63 
64 static int make_char_visible( FL_OBJECT * obj,
65                               int         xpos );
66 
67 static void copy_attributes( FL_OBJECT       * dest,
68                              const FL_OBJECT * src );
69 
70 static int date_validator( FL_OBJECT *,
71                            const char *,
72                            const char *,
73                            int );
74 
75 static int int_validator( FL_OBJECT *,
76                           const char *,
77                           const char *,
78                           int );
79 
80 static int float_validator( FL_OBJECT *,
81                             const char *,
82                             const char *,
83                             int );
84 
85 static int xytopos( FLI_INPUT_SPEC * sp,
86                     int              xpos,
87                     int              ypos );
88 
89 enum {
90     NORMAL_SELECT,
91     WORD_SELECT,
92     LINE_SELECT
93 };
94 
95 
96 static int Input_Mode = FL_NORMAL_INPUT_MODE;
97 
98 
99 /***************************************
100  ***************************************/
101 
102 static void
get_margin(int btype,int bw,FL_Coord * xm,FL_Coord * ym)103 get_margin( int        btype,
104             int        bw,
105             FL_Coord * xm,
106             FL_Coord * ym )
107 {
108     if (    btype == FL_FLAT_BOX
109          || btype == FL_NO_BOX
110          || btype == FL_FRAME_BOX
111          || btype == FL_EMBOSSED_BOX )
112     {
113         *xm = bw + 1;
114         *ym = 0.7 * bw + 1;
115     }
116     else
117     {
118         *xm = 2 * bw + ( bw == 1 );
119         *ym = bw + 1 + ( bw == 1 );
120     }
121 }
122 
123 
124 /***************************************
125  * Checks the size of scrollbars and input field.  No drawing is allowed
126  ***************************************/
127 
128 static void
check_scrollbar_size(FL_OBJECT * obj)129 check_scrollbar_size( FL_OBJECT * obj )
130 {
131     FLI_INPUT_SPEC *sp = obj->spec;
132     FL_Coord xmargin,
133              ymargin;
134     int bw = FL_abs( obj->bw );
135     int delta;
136     int h_on = sp->h_on,
137         v_on = sp->v_on;
138     int max_pixels = sp->max_pixels;
139 
140     /* The test for sp->vscroll and sp->hscroll being set is only for
141        fdesign which might change the object type of a normal input to
142        a multiline input without creating them */
143 
144     if (    sp->input->type != FL_MULTILINE_INPUT
145          || ! ( sp->vscroll && sp->hscroll ) )
146         return;
147 
148     /* Compute the real input box size */
149 
150     sp->input->x = sp->dummy->x;
151     sp->input->y = sp->dummy->y;
152 
153     get_margin( sp->input->boxtype, bw, &xmargin, &ymargin );
154     sp->charh = fl_get_char_height( sp->input->lstyle, sp->input->lsize, 0, 0 );
155 
156     /* See how many lines would fit in */
157 
158     sp->screenlines = ( sp->dummy->h - 2.0 * ymargin ) / sp->charh + 0.001;
159 
160     sp->v_on =    sp->v_pref == FL_ON
161                || (    sp->screenlines && sp->screenlines < sp->lines
162                     && sp->v_pref != FL_OFF );
163 
164     if ( sp->v_on )
165     {
166         sp->vw = sp->vw_def;
167         sp->vscroll->x = sp->input->x + sp->dummy->w - sp->vw;
168         sp->vscroll->y = sp->input->y;
169         sp->vscroll->w = sp->vw;
170         fli_set_object_visibility( sp->vscroll, FL_VISIBLE );
171     }
172     else
173     {
174         sp->vw = 0;
175         fli_set_object_visibility( sp->vscroll, FL_INVISIBLE );
176     }
177 
178     sp->input->w = sp->dummy->w - sp->vw;
179 
180     sp->h_on =    sp->h_pref == FL_ON
181                || (    max_pixels > sp->w
182                     && sp->h_pref != FL_OFF );
183 
184     if ( sp->h_on )
185     {
186         sp->h_on = 1;
187         sp->hh = sp->hh_def;
188         sp->hscroll->x = sp->input->x;
189         sp->hscroll->y = sp->input->y + sp->dummy->h - sp->hh;
190         sp->hscroll->h = sp->hh;
191         fli_set_object_visibility( sp->hscroll, FL_VISIBLE );
192 
193         if ( ( delta = max_pixels - sp->w ) > 0 )
194         {
195             sp->hsize = ( double ) sp->w / max_pixels;
196             sp->hval  = ( double ) sp->xoffset / delta;
197             sp->hinc1 = 8.0 * sp->charh / delta;
198             sp->hinc2 = ( sp->charh - 2.0 ) / delta;
199         }
200         else
201             sp->hsize = 1.0;
202     }
203     else
204     {
205         sp->hh = 0;
206         fli_set_object_visibility( sp->hscroll, FL_INVISIBLE );
207     }
208 
209     sp->input->h = sp->dummy->h - sp->hh;
210     sp->h = sp->input->h - 2 * ymargin;
211 
212     sp->screenlines = ( double ) sp->h / sp->charh + 0.001;
213 
214     if ( ! sp->v_on && sp->screenlines < sp->lines && sp->h_pref != FL_OFF )
215     {
216         sp->v_on = 1;
217         sp->vw = sp->vw_def;
218         sp->vscroll->x = sp->input->x + sp->dummy->w - sp->vw;
219         sp->vscroll->y = sp->input->y;
220         sp->vscroll->visible = 1;
221         sp->input->w = sp->dummy->w - sp->vw;
222     }
223 
224     if ( sp->v_on && ( delta = sp->lines - sp->screenlines ) > 0 )
225     {
226         sp->vval = ( sp->topline - 1.0 ) / delta;
227         sp->vsize = ( double ) sp->screenlines / sp->lines;
228         sp->vinc1 = ( sp->screenlines - 0.99 ) / delta;
229         sp->vinc2 = 1.01 / delta;
230     }
231     else
232         sp->vsize = 1.0;
233 
234     sp->hscroll->w = sp->input->w;
235     fli_notify_object( sp->hscroll, FL_RESIZED );
236     sp->vscroll->h = sp->input->h;
237     fli_notify_object( sp->vscroll, FL_RESIZED );
238     sp->w = sp->input->w - 2 * xmargin;
239 
240     if ( h_on != sp->h_on || v_on != sp->v_on )
241     {
242         sp->attrib = 1;
243         sp->dead_area = ! ( sp->h_on ^ sp->v_on );
244     }
245 }
246 
247 
248 /***************************************
249  ***************************************/
250 
251 static void
draw_input(FL_OBJECT * obj)252 draw_input( FL_OBJECT * obj )
253 {
254     FLI_INPUT_SPEC *sp = obj->spec;
255     FL_COLOR col;
256     FL_COLOR curscol = fli_dithered( fl_vmode ) ? FL_BLACK : sp->curscol;
257     FL_Coord xmargin,
258              ymargin;
259     int bw = FL_abs( obj->bw );
260     int cx,
261         cy;
262     int max_pixels,
263         max_pixels_line;
264     static char *saved;
265 
266     get_margin( obj->boxtype, bw, &xmargin, &ymargin );
267     sp->w = sp->input->w - 2 * xmargin;
268     sp->h = sp->input->h - 2 * ymargin;
269 
270     col = obj->focus ? obj->col2 : obj->col1;
271 
272     if ( sp->drawtype == COMPLETE )
273     {
274         fl_draw_box( obj->boxtype, sp->input->x, sp->input->y,
275                      sp->input->w, sp->input->h, col, obj->bw );
276         fl_draw_object_label_outside( obj );
277     }
278 
279     if ( obj->type == FL_SECRET_INPUT )
280     {
281         saved = sp->str;
282         sp->str = fl_strdup( sp->str );
283         memset( sp->str, sp->field_char, strlen( saved ) );
284     }
285 
286     cx = sp->input->x + xmargin;
287     cy = sp->input->y + ymargin;
288 
289     fl_set_text_clipping( cx, cy, sp->w, sp->h );
290     fl_set_clipping( cx, cy, sp->w, sp->h );
291 
292     max_pixels = fli_draw_string( obj->type == FL_MULTILINE_INPUT ?
293                                   FL_ALIGN_LEFT_TOP : FL_ALIGN_LEFT,
294                                   cx - sp->xoffset,      /* Bounding box */
295                                   cy - sp->yoffset,
296                                   sp->w + sp->xoffset,
297                                   sp->h + sp->yoffset,
298                                  -1,               /* Clipping is already set */
299                                   col, sp->textcol, curscol,
300                                   obj->lstyle, obj->lsize,
301                                   (    sp->cursor_visible
302                                     && obj->focus
303                                     && sp->beginrange >= sp->endrange ) ?
304                                   sp->position : -1,
305                                   sp->beginrange, sp->endrange,
306                                   sp->str, sp->drawtype != COMPLETE,
307                                   sp->topline,
308                                   sp->topline + sp->screenlines, 0 );
309 
310     max_pixels_line = fli_get_max_pixels_line( ) + 1;
311     sp->charh = fl_get_char_height( obj->lstyle, obj->lsize, 0, 0 );
312 
313     if (    max_pixels > sp->max_pixels
314          || (    sp->max_pixels_line >= sp->topline
315               && sp->max_pixels_line <= sp->topline + sp->screenlines ) )
316     {
317         sp->max_pixels = max_pixels;
318         sp->max_pixels_line = max_pixels_line;
319     }
320 
321     fl_unset_clipping( );
322     fl_unset_text_clipping( );
323 
324     if ( obj->type == FL_SECRET_INPUT )
325     {
326         fli_safe_free( sp->str );
327         sp->str = saved;
328     }
329 
330     sp->drawtype = COMPLETE;
331 }
332 
333 
334 #define DELIM( c )  ( c == ' ' || c == ',' || c == '.' || c == '\n' )
335 
336 
337 /***************************************
338  * Figures out selection region of mouse, returns whether anything changed
339  ***************************************/
340 
341 static int
handle_select(FL_Coord mx,FL_Coord my,FL_OBJECT * obj,int movement,int mode)342 handle_select( FL_Coord    mx,
343                FL_Coord    my,
344                FL_OBJECT * obj,
345                int         movement,
346                int         mode )
347 {
348     FLI_INPUT_SPEC *sp = obj->spec;
349     int thepos,
350         n,
351         dummy;
352     int oldpos = sp->position,
353         oldbeg = sp->beginrange,
354         oldend = sp->endrange;
355     int bw = FL_abs( obj->bw );
356     FL_Coord xmargin,
357              ymargin;
358 
359     if ( obj->type == FL_HIDDEN_INPUT )
360         return 0;
361 
362     /* Compute the mouse position in the string */
363 
364     get_margin( obj->boxtype, bw, &xmargin, &ymargin );
365 
366     thepos = fli_get_pos_in_string( obj->type == FL_MULTILINE_INPUT ?
367                                     FL_ALIGN_LEFT_TOP : FL_ALIGN_LEFT,
368                                     sp->input->x + xmargin - sp->xoffset,
369                                     sp->input->y + ymargin - sp->yoffset,
370                                     sp->w + sp->xoffset,
371                                     sp->h + sp->yoffset,
372                                     obj->lstyle, obj->lsize,
373                                     mx, my, sp->str,
374                                     &sp->xpos, &sp->ypos, &dummy );
375 
376     if ( mode == WORD_SELECT )
377     {
378 #if defined USE_CLASSIC_EDITKEYS
379         if ( sp->str[ thepos ] == ' ' )
380             return 0;
381 
382         for ( n = thepos; sp->str[ n ] && ! DELIM( sp->str[ n ] ); n++ )
383             /* empty */ ;
384         sp->endrange = n;
385 
386         for ( n = thepos; n >= 0 && ! DELIM( sp->str[ n ] ); n-- )
387             /* empty */ ;
388 
389         sp->beginrange = n + 1;
390 #else
391         if (    ! isalnum( ( unsigned char ) sp->str[ thepos ] )
392              && sp->str[ thepos ] != '_' )
393         {
394             for ( n = thepos;
395                      sp->str[ n ]
396                   && ! isalnum( ( unsigned char ) sp->str[ n ] )
397                   && sp->str[ n ] != '_'
398                   && sp->str[ n ] != '\n';
399                   n++ )
400                 /* empty */ ;
401             sp->endrange = n;
402 
403             for ( n = thepos;
404                      n
405                   && ! isalnum( ( unsigned char ) sp->str[ n ] )
406                   && sp->str[ n ] != '_'
407                   && sp->str[ n ] != '\n';
408                   n-- )
409                 /* empty */ ;
410             if ( n > 0 )
411                 ++n;
412             sp->beginrange = n;
413         }
414         else
415         {
416             for ( n = thepos;
417                      sp->str[ n ]
418                   && (    isalnum( ( unsigned char ) sp->str[ n ] )
419                        || sp->str[ n ] == '_' );
420                   n++ )
421                 /* empty */ ;
422             sp->endrange = n;
423 
424             for ( n = thepos;
425                   n && (    isalnum( ( unsigned char ) sp->str[ n ] )
426                          || sp->str[ n ] == '_' );
427                   n-- )
428                 /* empty */ ;
429             if ( n > 0 )
430                 ++n;
431             sp->beginrange = n;
432         }
433 #endif
434     }
435     else if ( mode == LINE_SELECT )
436     {
437         for ( n = thepos; sp->str[ n ] && sp->str[ n ] != '\n'; n++ )
438             /* empty */ ;
439         sp->endrange = n;
440 
441         for ( n = thepos; n >= 0 && sp->str[ n ] != '\n'; n-- )
442             /* empty */ ;
443         sp->beginrange = n + 1;
444     }
445     else
446     {
447         /* Adapt the range */
448 
449         if ( movement )
450         {
451             fl_freeze_form( obj->form );
452             make_line_visible( obj, sp->ypos );
453             make_char_visible( obj, sp->xpos );
454             fl_unfreeze_form( obj->form );
455 
456             if ( thepos < sp->position )
457             {
458                 sp->endrange = sp->position;
459                 sp->beginrange = thepos;
460             }
461             else
462             {
463                 sp->beginrange = sp->position;
464                 sp->endrange = thepos;
465             }
466         }
467         else
468         {
469             sp->position = sp->beginrange = thepos;
470             sp->endrange = -1;
471         }
472     }
473 
474     if ( sp->beginrange == sp->endrange )
475         sp->endrange = -1;
476 
477     if ( sp->beginrange < 0 )
478         sp->beginrange = 0;
479 
480     return    oldpos != sp->position
481            || oldbeg != sp->beginrange
482            || oldend != sp->endrange;
483 }
484 
485 
486 /***************************************
487  * This is not XCUTBUFFER. It is generated by ^K and can be
488  * recalled by ^Y
489  ***************************************/
490 
491 #define MAXCBLEN   512
492 static char cutbuf[ MAXCBLEN ];
493 
494 
495 /***************************************
496  * Delete a single char. dir =1 for next, dir -1 for prev
497  ***************************************/
498 
499 static void
delete_char(FLI_INPUT_SPEC * sp,int dir,int slen)500 delete_char( FLI_INPUT_SPEC * sp,
501              int              dir,
502              int              slen )
503 {
504     int i = sp->position - ( dir < 0 );
505 
506     if ( sp->str[ i ] == '\n' )
507     {
508         sp->lines--;
509         sp->ypos -= dir < 0;
510     }
511 
512     memmove( sp->str + i, sp->str + i + 1, slen - i );
513     sp->position -= dir < 0;
514 }
515 
516 
517 /***************************************
518  * Removes a piece of the string
519  ***************************************/
520 
521 static void
delete_piece(FL_OBJECT * obj,int start,int end)522 delete_piece( FL_OBJECT * obj,
523               int         start,
524               int         end )
525 {
526     FLI_INPUT_SPEC *sp = obj->spec;
527 
528     memmove( sp->str + start, sp->str + end + 1, strlen( sp->str + end ) );
529     sp->position = start;
530 
531     /* This can be expensive TODO */
532 
533     sp->lines = fl_get_input_numberoflines( obj );
534     fl_get_input_cursorpos( obj, &sp->xpos, &sp->ypos );
535 }
536 
537 
538 #define get_substring_width( o, b, e )                                       \
539     fl_get_string_width( ( o )->lstyle, ( o )->lsize,                        \
540                          ( ( FLI_INPUT_SPEC * ) ( o )->spec )->str + ( b ),  \
541                          ( e ) - ( b ) )
542 
543 #define IsRegular( k )  (    ( k ) == '\n'                                \
544                           || ( key >= 32 && key <= 255 && key != 127 ) )
545 
546 
547 /***************************************
548  * Editing command. Need 4 bytes. Byte1 for normal ASCII, byte2 for
549  * special keysyms, such as PageUP etc. Byte 3 is used by Latin3 etc.
550  * Byte 4 will be used to indicate modifiers.
551  ***************************************/
552 
553 static FL_EditKeymap kmap;
554 
555 static int paste_it( FL_OBJECT *,
556                      const unsigned char *,
557                      int );
558 
559 static void set_default_keymap( int );
560 
561 #define set_to_eol( p )  while ( ( p ) < slen && sp->str[ p ] != '\n' ) ( p )++
562 
563 
564 /***************************************
565  * Cursor moved. No editing.
566  ***************************************/
567 
568 static void
handle_movement(FL_OBJECT * obj,int key,int slen,int startpos,int kmask)569 handle_movement( FL_OBJECT * obj,
570                  int         key,
571                  int         slen,
572                  int         startpos,
573                  int         kmask )
574 {
575     FLI_INPUT_SPEC *sp = obj->spec;
576     int ready,
577         width,
578         i,
579         oldwidth,
580         tt;
581 
582     if ( IsHome( key ) )
583     {
584         fl_set_input_topline( obj, 1 );
585         sp->position = 0;
586         sp->ypos = 1;
587     }
588     else if ( IsPageDown( key ) )
589         fl_set_input_topline( obj, sp->topline + sp->screenlines );
590     else if ( IsHalfPageDown( key ) )
591         fl_set_input_topline( obj, sp->topline + sp->screenlines / 2 );
592     else if ( Is1LineDown( key ) )
593         fl_set_input_topline( obj, sp->topline + 1 );
594     else if ( IsPageUp( key ) )
595         fl_set_input_topline( obj, sp->topline - sp->screenlines );
596     else if ( IsHalfPageUp( key ) )
597         fl_set_input_topline( obj, sp->topline - sp->screenlines / 2 );
598     else if ( Is1LineUp( key ) )
599         fl_set_input_topline( obj, sp->topline - 1 );
600     else if ( key == '\t' || key == '\f' )
601         /* empty */ ;
602     else if ( IsEnd( key ) )
603     {
604         fl_set_input_topline( obj, sp->lines );
605         sp->position = slen;
606         fl_get_input_cursorpos( obj, &sp->xpos, &sp->ypos );
607     }
608     else if ( IsLeft( key ) )   /* Left key */
609     {
610         if ( shiftkey_down( kmask ) )
611             sp->position = startpos;
612         else if ( sp->position > 0 )
613             sp->position--;
614 
615         if ( sp->str[ sp->position ] == '\n' )
616         {
617             sp->ypos--;
618 
619             /* Compute starting position of current line */
620 
621             startpos = sp->position;
622             while ( startpos > 0 && sp->str[ startpos - 1 ] != '\n' )
623                 startpos--;
624         }
625     }
626     else if ( IsRight( key ) || key == kmap.moveto_eol )
627     {
628         if ( shiftkey_down( kmask ) || key == kmap.moveto_eol )
629             set_to_eol( sp->position );
630         else if ( sp->position < slen )
631         {
632             if ( sp->str[ sp->position ] == '\n' )
633             {
634                 sp->ypos++;
635                 startpos = sp->position + 1;
636             }
637             sp->position++;
638         }
639     }
640     else if ( IsUp( key ) )     /* Up key */
641     {
642         if ( startpos != 0 )
643         {
644             width = get_substring_width( obj, startpos, sp->position );
645             i = startpos - 1;
646 
647             while ( i > 0 && sp->str[ i - 1 ] != '\n' )
648                 i--;
649 
650             oldwidth = 0.0;
651             sp->position = i;
652 
653             ready = sp->str[ sp->position ] == '\n';
654 
655             while ( ! ready )
656             {
657                 tt = get_substring_width( obj, i, sp->position + 1 );
658                 ready = 0.5 * ( oldwidth + tt ) >= width;
659                 oldwidth = tt;
660 
661                 if ( ! ready )
662                     sp->position++;
663 
664                 if ( sp->str[ sp->position ] == '\n' )
665                     ready = 1;
666             }
667 
668             if ( --sp->ypos < 1 )
669                 sp->ypos = 1;
670         }
671     }
672     else if ( IsDown( key ) )   /* Down key */
673     {
674         width = get_substring_width( obj, startpos, sp->position );
675         i = sp->position + 1;
676 
677         while ( i < slen && sp->str[ i - 1 ] != '\n' )
678             i++;
679 
680         if ( i < slen )
681         {
682             oldwidth = 0.0;
683             sp->position = i;
684             ready = sp->position == slen || sp->str[ sp->position ] == '\n';
685 
686             while ( ! ready )
687             {
688                 tt = get_substring_width( obj, i, sp->position + 1 );
689                 ready = 0.5 * ( oldwidth + tt ) >= width;
690                 oldwidth = tt;
691 
692                 if ( ! ready )
693                     sp->position++;
694 
695                 if ( sp->position == slen || sp->str[ sp->position ] == '\n' )
696                     ready = 1;
697             }
698         }
699         else
700             sp->position = slen;
701 
702         if ( ++sp->ypos > sp->lines )
703             sp->ypos = sp->lines;
704     }
705     else if ( key == kmap.moveto_bol )
706         sp->position = startpos;
707     else if ( key == kmap.moveto_prev_word )
708     {
709 #if defined USE_CLASSIC_EDITKEYS
710         if ( sp->position > 0 )
711             sp->position--;
712 
713         while (    sp->position > 0
714                 && (    sp->str[ sp->position ] == ' '
715                      || sp->str[ sp->position ] == '\n' ) )
716         {
717             if ( sp->str[ sp->position ] == '\n' )
718                 sp->ypos--;
719 
720             sp->position--;
721         }
722 
723         while (    sp->position > 0
724                 && sp->str[ sp->position ] != ' '
725                 && sp->str[ sp->position ] != '\n' )
726             sp->position--;
727 #else
728         if ( sp->position > 0 )
729             sp->position--;
730 
731         if (     ! isalnum( ( unsigned char ) sp->str[ sp->position ] )
732               && sp->str[ sp->position ] != '_' )
733             while (    sp->position > 0
734                     && ! (    isalnum( ( unsigned char )
735                                        sp->str[ sp->position ] )
736                            || sp->str[ sp->position ] == '_' ) )
737                 --sp->position;
738         else
739             while (    sp->position > 0
740                     && (    isalnum( ( unsigned char ) sp->str[ sp->position ] )
741                          || sp->str[ sp->position ] == '_' ) )
742                 --sp->position;
743 #endif
744 
745         if ( sp->position > 0 )
746             sp->position++;
747     }
748     else if ( key == kmap.moveto_next_word )
749     {
750         i = sp->position;
751 
752 #if defined USE_CLASSIC_EDITKEYS
753         while ( i < slen && ( sp->str[ i ] == ' ' || sp->str[ i ] == '\n' ) )
754         {
755             if ( sp->str[ i ] == '\n' )
756                 sp->ypos++;
757             i++;
758         }
759 
760         while ( i < slen && sp->str[ i ] != ' ' && sp->str[ i ] != '\n' )
761             i++;
762 #else
763         if (    ! isalnum( ( unsigned char ) sp->str[ i ] )
764              && sp->str[ i ] != '_' )
765             while (    i < slen
766                     && ! isalnum( ( unsigned char ) sp->str[ i ] )
767                     && sp->str[ i ] != '_' )
768                 ++i;
769         else
770             while (    i < slen
771                     && (    isalnum( ( unsigned char ) sp->str[ i ] )
772                          || sp->str[ i ] == '_' ) )
773                 ++i;
774 #endif
775         sp->position = i;
776     }
777 }
778 
779 
780 /***************************************
781  * Editing
782  ***************************************/
783 
784 static int
handle_edit(FL_OBJECT * obj,int key,int slen)785 handle_edit( FL_OBJECT * obj,
786              int         key,
787              int         slen )
788 {
789     FLI_INPUT_SPEC *sp = obj->spec;
790     int ret = FL_RETURN_CHANGED,
791         i;
792 
793     if ( key == kmap.del_prev_char || key == kmap.backspace )
794     {
795         if ( sp->endrange >= 0 )
796             delete_piece( obj, sp->beginrange, sp->endrange - 1 );
797         else if ( sp->position > 0 )
798             delete_char( sp, -1, slen );
799         else
800             ret = FL_RETURN_NONE;
801     }
802     else if ( key == kmap.del_next_char )
803     {
804         if ( sp->endrange >= 0 )
805             delete_piece( obj, sp->beginrange, sp->endrange - 1 );
806         else if ( sp->position < slen )
807             delete_char( sp, 1, slen );
808         else
809             ret = FL_RETURN_NONE;
810     }
811     else if ( key == kmap.del_next_word )
812     {
813         if ( obj->type == FL_SECRET_INPUT || ( i = sp->position ) == slen )
814             ret = FL_RETURN_NONE;
815         else
816         {
817 #if defined USE_CLASSIC_EDITKEYS
818             while (    i < slen
819                     && ( sp->str[ i ] == ' ' || sp->str[ i ] == '\n' ) )
820                 i++;
821             while ( i < slen && sp->str[ i ] != ' ' && sp->str[ i ] != '\n' )
822                 i++;
823 #else
824             /* If the first character is neiter a letter nor a digit nor an
825                underscore delete it and all other characters of the same kind.
826                Otherwise delete all charaters that are letters. digit or
827                underscores. This is the same behaviour as in e.g. Qt.
828             */
829 
830             if (    ! isalnum( ( unsigned char ) sp->str[ i ] )
831                  && sp->str[ i ] != '_' )
832                 while (    i < slen
833                         && ! isalnum( ( unsigned char ) sp->str[ i ] )
834                         && sp->str[ i ] != '_' )
835                     i++;
836             else
837                 while (    i < slen
838                         && (    isalnum( ( unsigned char ) sp->str[ i ] )
839                              || sp->str[ i ] == '_' ) )
840                     i++;
841 #endif
842             if ( i - sp->position > 1 )
843                 fli_sstrcpy( cutbuf, sp->str + sp->position,
844                              FL_min( i - sp->position + 1, MAXCBLEN ) );
845 
846             delete_piece( obj, sp->position, i - 1 );
847         }
848     }
849     else if ( key == kmap.del_prev_word )
850     {
851         int j = sp->position;
852 
853         if ( obj->type == FL_SECRET_INPUT || j == 0 )
854             ret = FL_RETURN_NONE;
855         else
856         {
857 
858 #if defined USE_CLASSIC_EDITKEYS
859             sp->position--;
860             while (    sp->position > 0
861                     && (    sp->str[ sp->position ] == ' '
862                          || sp->str[ sp->position ] == '\n' ) )
863                 sp->position--;
864             while (    sp->position > 0
865                     && sp->str[ sp->position ] != ' '
866                     && sp->str[ sp->position ] != '\n' )
867                 sp->position--;
868 #else
869             --sp->position;
870             if (    ! isalnum( ( unsigned char ) sp->str[ sp->position ] )
871                  && sp->str[ sp->position ] != '_' )
872                 while (    sp->position > 0
873                         && ! isalnum( ( unsigned char )
874                                       sp->str[ sp->position ] )
875                         && sp->str[ sp->position ] != '_' )
876                     --sp->position;
877             else
878                 while (    sp->position > 0
879                         && (    isalnum( ( unsigned char )
880                                          sp->str[ sp->position ] )
881                              || sp->str[ sp->position ] == '_' ) )
882                     --sp->position;
883 
884             if ( sp->position )
885                 ++sp->position;
886 #endif
887             if ( sp->position != j )
888             {
889                 if ( j - sp->position > 1 )
890                     fli_sstrcpy( cutbuf, sp->str + sp->position,
891                                  FL_min( j - sp->position + 1, MAXCBLEN ) );
892                 delete_piece( obj, sp->position, j - 1 );
893             }
894             else
895                 ret = FL_RETURN_NONE;
896         }
897     }
898     else if ( key == kmap.clear_field )
899     {
900         if ( slen > 0 )
901         {
902             if ( slen > 1 )
903                 fli_sstrcpy( cutbuf, sp->str, FL_min( slen, MAXCBLEN ) );
904             delete_piece( obj, 0, slen - 1 );
905         }
906         else
907             ret = FL_RETURN_NONE;
908     }
909     else if ( key == kmap.del_to_eol )
910     {
911         if ( slen > sp->position )
912         {
913             if ( sp->str[ sp->position ] != '\n' )
914                 for ( i = sp->position; i < slen && sp->str[ i ] != '\n'; i++ )
915                     /* empty */ ;
916             else
917                 i = sp->position + 1;
918 
919             /* Save buffer */
920 
921             if ( i - sp->position > 1 )
922                 fli_sstrcpy( cutbuf, sp->str + sp->position,
923                              FL_min( i - sp->position + 1, MAXCBLEN ) );
924 
925             delete_piece( obj, sp->position, i - 1 );
926         }
927         else
928             ret = FL_RETURN_NONE;
929     }
930 #if ! defined USE_CLASSIC_EDITKEYS
931     else if ( key == kmap.del_to_bol )
932     {
933         int j = sp->position;
934 
935         if ( j == 0 )
936             ret = FL_RETURN_NONE;
937         else
938         {
939             if ( sp->str[ --sp->position ] != '\n' )
940             {
941                 while ( sp->position > 0 && sp->str[ --sp->position ] != '\n' )
942                     /* empty */;
943                 if ( sp->str[ sp->position ] == '\n' )
944                     ++sp->position;
945             }
946 
947             if ( j - sp->position > 1 )
948                 fli_sstrcpy( cutbuf, sp->str + sp->position,
949                              FL_min( j - sp->position + 1, MAXCBLEN ) );
950 
951             delete_piece( obj, sp->position, j - 1 );
952         }
953     }
954 #endif
955     else if ( key == kmap.paste )
956         paste_it( obj, ( unsigned char * ) cutbuf, strlen( cutbuf ) );
957     else if ( key == kmap.transpose && sp->position > 0 )
958     {
959         char t;
960 
961         if ( sp->position < slen && sp->str[ sp->position ] != '\n' )
962         {
963             t = sp->str[ sp->position - 1 ];
964             sp->str[ sp->position - 1 ] = sp->str[ sp->position ];
965             sp->str[ sp->position ] = t;
966             sp->position++;
967         }
968         else
969         {
970             t = sp->str[ sp->position - 2 ];
971             sp->str[ sp->position - 2 ] = sp->str[ sp->position - 1 ];
972             sp->str[ sp->position - 1 ] = t;
973         }
974     }
975 
976     return ret;
977 }
978 
979 
980 /***************************************
981  * Handles a key press, returns whether something has changed
982  ***************************************/
983 
984 static int
handle_normal_key(FL_OBJECT * obj,int key,int slen)985 handle_normal_key( FL_OBJECT    * obj,
986                    int            key,
987                    int            slen )
988 {
989     FLI_INPUT_SPEC *sp = obj->spec;
990     char *tmpbuf = NULL;
991     int tmppos = 0;
992     int ret = FL_RETURN_CHANGED;
993 
994     /* Check that there's still room for a new character */
995 
996     if (    sp->maxchars > 0
997          && slen >= sp->maxchars
998          && (    Input_Mode == FL_NORMAL_INPUT_MODE
999               || slen == sp->position ) )
1000     {
1001         fl_ringbell( 0 );
1002         return FL_RETURN_NONE;
1003     }
1004 
1005     if ( sp->validate )
1006     {
1007         tmpbuf = fl_strdup( sp->str );
1008         tmppos = sp->position;
1009     }
1010 
1011     /* If a range is marked remove it, it's replaced by the new character */
1012 
1013     if ( sp->endrange >= 0 )
1014     {
1015         delete_piece( obj, sp->beginrange, sp->endrange - 1 );
1016         slen = strlen( sp->str );
1017     }
1018 
1019     /* Merge the new character */
1020 
1021     if (    Input_Mode == FL_DOS_INPUT_MODE
1022         && sp->maxchars > 0
1023         && slen == sp->maxchars )
1024     {
1025         memmove( sp->str + sp->position + 1, sp->str + sp->position,
1026                  slen - sp->position );
1027         sp->str[ sp->maxchars ] = '\0';
1028     }
1029     else
1030         memmove( sp->str + sp->position + 1, sp->str + sp->position,
1031                  slen - sp->position + 1 );
1032     sp->str[ sp->position++ ] = key;
1033 
1034     if ( key == '\n' )
1035     {
1036         sp->lines++;
1037         sp->ypos++;
1038     }
1039 
1040     if ( sp->validate )
1041     {
1042         int ok = sp->validate( obj, tmpbuf, sp->str, key );
1043 
1044         if ( ( ok & ~ FL_RINGBELL ) != FL_VALID )
1045         {
1046             ret = FL_RETURN_NONE;
1047             strcpy( sp->str, tmpbuf );
1048             sp->position = tmppos;
1049 
1050             if ( key == '\n' )
1051             {
1052                 sp->lines--;
1053                 sp->ypos--;
1054             }
1055         }
1056 
1057         if ( ok & FL_RINGBELL )
1058             fl_ringbell( 0 );
1059         fl_free( tmpbuf );
1060     }
1061 
1062     return ret;
1063 }
1064 
1065 
1066 
1067 /***************************************
1068  * Handles a key press, returns whether the content of the input field
1069  * was changed
1070  ***************************************/
1071 
1072 static int
handle_key(FL_OBJECT * obj,int key,unsigned int kmask)1073 handle_key( FL_OBJECT    * obj,
1074             int            key,
1075             unsigned int   kmask )
1076 {
1077     int ret = FL_RETURN_NONE;
1078     FLI_INPUT_SPEC *sp = obj->spec;
1079     int slen;                  /* length of the string */
1080     int oldy = sp->ypos;
1081     int oldl = sp->lines;
1082     int oldx = sp->xoffset;
1083     int oldmax = sp->max_pixels;
1084 
1085     /* Increase the size of the buffer for the text if it's full */
1086 
1087     slen = strlen( sp->str );
1088 
1089     if ( sp->size == slen + 1 )
1090     {
1091         sp->size += 8;
1092         sp->str = fl_realloc( sp->str, sp->size );
1093     }
1094 
1095     /* Silently translate carriage return to line feed */
1096 
1097     if ( obj->type == FL_MULTILINE_INPUT && key == '\r' )
1098         key = '\n';
1099 
1100 #if defined USE_CLASSIC_EDITKEYS
1101     if ( controlkey_down( kmask ) && key > 255 )
1102         key |= FL_CONTROL_MASK;
1103 #else
1104     if ( controlkey_down( kmask ) )
1105         key |= FL_CONTROL_MASK;
1106 #endif
1107 
1108     if ( metakey_down( kmask ) )
1109         key |= FL_ALT_MASK;
1110 
1111     if ( shiftkey_down( kmask ) )
1112     {
1113         if ( key == XK_Up )
1114             key = XK_Home;
1115         else if ( key == XK_Down )
1116             key = XK_End;
1117     }
1118 
1119     /* Translate all move keys to cursor keys so we can distinguish edit/move
1120        keys more easily */
1121 
1122     if ( key == kmap.moveto_next_line )
1123         key = XK_Down;
1124     else if ( key == kmap.moveto_prev_line )
1125         key = XK_Up;
1126     else if ( key == kmap.moveto_prev_char )
1127         key = XK_Left;
1128     else if ( key == kmap.moveto_next_char )
1129         key = XK_Right;
1130     else if ( key == kmap.moveto_bof )
1131         key = XK_Home;
1132     else if ( key == kmap.moveto_eof )
1133         key = XK_End;
1134     else if ( key == kmap.moveto_next_page )
1135         key = XK_PageDn;
1136     else if ( key == kmap.moveto_prev_page )
1137         key = XK_PageUp;
1138 
1139     if ( IsRegular( key ) )     /* Normal keys and new line */
1140         ret = handle_normal_key( obj, key, slen );
1141     else if (    IsCursorKey( key )
1142               || key == kmap.moveto_eol
1143               || key == kmap.moveto_bol
1144               || key == kmap.moveto_prev_word
1145               || key == kmap.moveto_next_word
1146               || Is1LineUp( key )
1147               || Is1LineDown( key )
1148               || IsHalfPageUp( key )
1149               || IsHalfPageDown( key ) )
1150     {
1151         int startpos = 0;
1152 
1153         if ( obj->type == FL_MULTILINE_INPUT )
1154         {
1155             startpos = sp->position;
1156             while ( startpos > 0 && sp->str[ startpos - 1 ] != '\n' )
1157                 startpos--;
1158         }
1159 
1160         handle_movement( obj, key, slen, startpos, kmask );
1161 
1162         if ( sp->endrange != -1 )
1163         {
1164             make_line_visible( obj, sp->ypos );
1165             make_char_visible( obj, sp->xpos );
1166         }
1167     }
1168     else
1169         ret = handle_edit( obj, key, slen );
1170 
1171     sp->endrange = -1;
1172 
1173     if ( ret != FL_RETURN_NONE )
1174     {
1175         int junk;
1176 
1177         fl_get_string_dimension( obj->lstyle, obj->lsize, sp->str,
1178                                  strlen( sp->str ), &sp->max_pixels, &junk );
1179     }
1180 
1181     if ( sp->noscroll )
1182     {
1183         sp->xoffset = sp->yoffset = 0;
1184         sp->topline = sp->ypos = 1;
1185         oldmax = sp->max_pixels;
1186     }
1187     else
1188     {
1189         int startpos = 0;
1190         int width;
1191 
1192         if ( obj->type == FL_MULTILINE_INPUT )
1193         {
1194             startpos = sp->position;
1195             while ( startpos > 0 && sp->str[ startpos - 1 ] != '\n' )
1196                 startpos--;
1197         }
1198 
1199         width = fl_get_string_width( obj->lstyle, obj->lsize,
1200                                      sp->str + startpos,
1201                                      sp->position - startpos );
1202 
1203         if ( width < sp->w - 4 )
1204             sp->xoffset = 0;
1205         else
1206         {
1207             if ( width - oldx > sp->w - 4 )
1208                 sp->xoffset = width - sp->w + 4;
1209             else
1210             {
1211                 int cw = fl_get_char_width( obj->lstyle, obj->lsize );
1212                 if ( width - oldx < cw )
1213                 sp->xoffset = width - cw;
1214                 else
1215                     sp->xoffset = oldx;
1216             }
1217         }
1218     }
1219 
1220     fl_freeze_form( obj->form );
1221 
1222     if (    oldl   != sp->lines
1223          || oldy   != sp->ypos
1224          || oldx   != sp->xoffset
1225          || oldmax != sp->max_pixels )
1226     {
1227         check_scrollbar_size( obj );
1228         make_line_visible( obj, sp->ypos );
1229         redraw_scrollbar( obj );
1230     }
1231 
1232     fl_redraw_object( sp->input );
1233     fl_unfreeze_form( obj->form );
1234 
1235     return ret;
1236 }
1237 
1238 
1239 /***************************************
1240  * Given nb bytes of stuff, paste it into the input field.
1241  ***************************************/
1242 
1243 static int
paste_it(FL_OBJECT * obj,const unsigned char * thebytes,int nb)1244 paste_it( FL_OBJECT           * obj,
1245           const unsigned char * thebytes,
1246           int                   nb )
1247 {
1248     int ret = FL_RETURN_NONE;
1249 
1250     while ( nb-- )
1251         ret |= handle_key( obj, *thebytes++, 0 );
1252 
1253     return ret;
1254 }
1255 
1256 
1257 /***************************************
1258  * Callback for handling selection. It might be called only after
1259  * handling the input object is done and in that case we have to
1260  * insert the object into the onject queue manually...
1261  ***************************************/
1262 
1263 /* handle X cut & paste ******************************* */
1264 
1265 static int selection_hack = 0;
1266 
1267 static int
gotit_cb(FL_OBJECT * obj,long type FL_UNUSED_ARG,const void * buf,long nb)1268 gotit_cb( FL_OBJECT  * obj,
1269           long         type  FL_UNUSED_ARG,
1270           const void * buf,
1271           long         nb )
1272 {
1273     FLI_INPUT_SPEC *sp = obj->spec;
1274 
1275     sp->changed |= paste_it( obj, ( unsigned char * ) buf, nb );
1276     fl_update_display( 0 );
1277 
1278     if ( selection_hack && sp->changed )
1279     {
1280         selection_hack = sp->changed = 0;
1281         obj->returned = FL_RETURN_CHANGED;
1282         fli_object_qenter( obj, FL_PASTE );
1283     }
1284 
1285     return 0;
1286 }
1287 
1288 
1289 /***************************************
1290  * Paste request is handled here. If we do not own the selection,
1291  * this will result in an SelectionNotify event that gets handled
1292  * by handle_clipboard_event(). And in that case we can't report
1293  * the object to have changed back to the application since all
1294  * that will happen some time later. Thus we set 'selection_hack'
1295  * to indicate to the getit_cb() function to artificially enter
1296  * the object into the object queue.
1297  ***************************************/
1298 
1299 static int
do_XPaste(FL_OBJECT * obj)1300 do_XPaste( FL_OBJECT * obj )
1301 {
1302     int ret = fl_request_clipboard( obj, XA_STRING, gotit_cb );
1303 
1304     if ( ret == -1 && obj->how_return == FL_RETURN_CHANGED )
1305         selection_hack = 1;
1306     return ret > 0 ? FL_RETURN_CHANGED : FL_RETURN_NONE;
1307 }
1308 
1309 
1310 /***************************************
1311  ***************************************/
1312 
1313 static int
lose_selection(FL_OBJECT * obj,long type FL_UNUSED_ARG)1314 lose_selection( FL_OBJECT * obj,
1315                 long        type  FL_UNUSED_ARG )
1316 {
1317     FLI_INPUT_SPEC *sp = obj->spec;
1318 
1319     sp->beginrange = sp->endrange = -1;
1320     if ( ! obj->focus )
1321         sp->position = -1;
1322     else if ( sp->position < 0 )
1323         sp->position = sp->str ? strlen( sp->str ) : 0;
1324     fl_redraw_object( sp->input );
1325     fl_update_display( 0 );
1326     return 0;
1327 }
1328 
1329 
1330 /***************************************
1331  ***************************************/
1332 
1333 static void
do_XCut(FL_OBJECT * obj,int beginrange,int endrange)1334 do_XCut( FL_OBJECT * obj,
1335          int         beginrange,
1336          int         endrange )
1337 {
1338     FLI_INPUT_SPEC *sp = obj->spec;
1339     char *buff;
1340     int nc = endrange - beginrange + 1;
1341 
1342     if ( nc <= 0 )
1343         return;
1344 
1345     buff = fl_malloc( nc + 1 );
1346 
1347     strncpy( buff, sp->str + beginrange, nc );
1348     buff[ nc ] = '\0';
1349 
1350     fl_stuff_clipboard( obj, XA_STRING, buff, nc, lose_selection );
1351 
1352     fl_free( buff );
1353 }
1354 
1355 
1356 /***************************************
1357  * Handles an event
1358  ***************************************/
1359 
1360 static int
handle_input(FL_OBJECT * obj,int event,FL_Coord mx,FL_Coord my,int key,void * ev)1361 handle_input( FL_OBJECT * obj,
1362               int         event,
1363               FL_Coord    mx,
1364               FL_Coord    my,
1365               int         key,
1366               void      * ev )
1367 {
1368     FLI_INPUT_SPEC *sp = obj->spec;
1369     static int motion,
1370                lx = INT_MAX,
1371                ly = INT_MAX,
1372                paste;
1373     int ret = FL_RETURN_NONE,
1374         val,
1375         state = 0;
1376 
1377     /* Convert scroll wheel events to up or down arrow key events */
1378 
1379     if ( event == FL_KEYPRESS )
1380         state = ( ( XKeyEvent * ) ev )->state;
1381 
1382     if ( event == FL_PUSH )
1383     {
1384         if ( key == FL_MBUTTON4 )
1385         {
1386             event = FL_KEYPRESS;
1387             key = XK_Up;
1388         }
1389         else if ( key == FL_MBUTTON5 )
1390         {
1391             event = FL_KEYPRESS;
1392             key = XK_Down;
1393         }
1394     }
1395 
1396     switch ( event )
1397     {
1398         case FL_ATTRIB :
1399             obj->align = fl_to_outside_lalign( obj->align );
1400             break;
1401 
1402         case FL_RESIZED :
1403             check_scrollbar_size( obj );
1404             break;
1405 
1406         case FL_DRAW:
1407             /* We always force label outside */
1408 
1409             if ( sp->input->type != FL_MULTILINE_INPUT )
1410             {
1411                 if ( sp->dummy != obj ) /* this can only happen with fdesign */
1412                     sp->dummy = sp->input = obj;
1413             }
1414 
1415             sp->dummy->align = fl_to_outside_lalign( sp->dummy->align );
1416             copy_attributes( sp->input, sp->dummy );
1417             if ( sp->input->type != FL_HIDDEN_INPUT )
1418                 draw_input( sp->input );
1419             /* fall through */
1420 
1421         case FL_DRAWLABEL:
1422             if ( sp->input->type != FL_MULTILINE_INPUT )
1423                 fl_draw_object_label_outside( sp->input );
1424             else
1425                 fl_draw_text_beside( sp->dummy->align,
1426                                      sp->input->x, sp->input->y,
1427                                      sp->input->w + sp->vw,
1428                                      sp->input->h + sp->hh,
1429                                      sp->input->lcol, sp->input->lstyle,
1430                                      sp->input->lsize, sp->dummy->label );
1431             break;
1432 
1433         case FL_FOCUS:
1434             if ( obj->type == FL_MULTILINE_INPUT )
1435             {
1436                 if ( sp->dummy->focus )
1437                     break;
1438                 sp->dummy->focus = 1;
1439             }
1440 
1441             // Put the cursor back into the position where it was (except
1442             // for DOS mode where it's always positioned at the start)
1443 
1444             if ( sp->str && Input_Mode != FL_DOS_INPUT_MODE )
1445             {
1446                 if ( sp->position < 0 )
1447                     sp->position = - sp->position - 1;
1448                 if ( sp->position > ( int ) strlen( sp->str ) )
1449                     sp->position = strlen( sp->str );
1450             }
1451             else
1452                 sp->position = 0;
1453 
1454             sp->changed = 0;
1455             fl_redraw_object( sp->input );
1456             break;
1457 
1458         case FL_UNFOCUS:
1459             if ( obj->type == FL_MULTILINE_INPUT )
1460                 sp->dummy->focus = 0;
1461 
1462             if ( sp->position >= 0 )
1463                 sp->position = - sp->position - 1;
1464             sp->endrange = -1;
1465             fl_redraw_object( sp->input );
1466 
1467             /* If the event is set to NULL don't validate or report
1468                any changes - the call came from either closing the
1469                form or from the user changing the focus with the
1470                fl_set_focus_object() function - never use the event,
1471                it may be invalid */
1472 
1473             if ( ev )
1474                 ret =   ( sp->changed ? FL_RETURN_CHANGED : FL_RETURN_NONE )
1475                       | FL_RETURN_END;
1476 
1477             break;
1478 
1479         case FL_UPDATE:
1480             if ( ! obj->focus )
1481                 break;
1482             motion = ( mx != lx || my != ly ) && ! paste;
1483             if ( motion && handle_select( mx, my, obj, 1, NORMAL_SELECT ) )
1484                 fl_redraw_object( sp->input );
1485             break;
1486 
1487         case FL_PUSH:
1488             paste = 0;
1489             lx = mx;
1490             ly = my;
1491             if ( key == FL_MBUTTON2 && ( sp->changed = do_XPaste( obj ) ) )
1492             {
1493                 if ( obj->how_return == FL_RETURN_CHANGED )
1494                     sp->changed = 0;
1495                 ret = FL_RETURN_CHANGED;
1496                 paste = 1;
1497             }
1498             else if ( handle_select( mx, my, obj, 0, NORMAL_SELECT ) )
1499                 fl_redraw_object( sp->input );
1500             break;
1501 
1502         case FL_RELEASE:
1503             if ( key == FL_MBUTTON4 || key == FL_MBUTTON5 )
1504                 break;
1505             if ( key == FL_MBUTTON1 && motion )
1506                 do_XCut( obj, sp->beginrange, sp->endrange - 1 );
1507             motion = 0;
1508             break;
1509 
1510         case FL_DBLCLICK:
1511         case FL_TRPLCLICK:
1512             if ( key == FL_MBUTTON4 || key == FL_MBUTTON5 )
1513                 break;
1514             if ( handle_select( mx, my, obj, 0,
1515                                 event == FL_DBLCLICK ?
1516                                 WORD_SELECT : LINE_SELECT ) )
1517             {
1518                 fl_redraw_object( sp->input );
1519                 do_XCut( obj, sp->beginrange, sp->endrange );
1520             }
1521             break;
1522 
1523         case FL_KEYPRESS :
1524             if ( ( ret = handle_key( obj, key, state ) ) )
1525             {
1526                 sp->changed = 1;
1527                 if ( obj->how_return == FL_RETURN_CHANGED )
1528                     sp->changed = 0;
1529             }
1530             break;
1531 
1532         case FL_FREEMEM:
1533             fli_safe_free( ( ( FLI_INPUT_SPEC * ) obj->spec )->str );
1534             fli_safe_free( obj->spec );
1535             return ret;
1536     }
1537 
1538     if (    ret
1539          && sp
1540          && sp->validate
1541          && event == FL_UNFOCUS
1542          && ( val = sp->validate( obj, sp->str, sp->str, 0 ) ) != FL_VALID )
1543     {
1544         ret = FL_RETURN_NONE;
1545         if ( val & FL_RINGBELL )
1546         {
1547             fl_ringbell( 0 );
1548             fl_reset_focus_object( obj );
1549         }
1550     }
1551 
1552     /* In some places the x- and y-coordinates (column and row) of the cursor
1553        aren't set consistently, so repair it here... */
1554 
1555     if ( obj->spec )
1556     fl_get_input_cursorpos( obj, &sp->xpos, &sp->ypos );
1557 
1558     return ret;
1559 }
1560 
1561 
1562 /***************************************
1563  * Callback for the vertical scrollbar of multi-line input objects
1564  ***************************************/
1565 
1566 static void
vsl_cb(FL_OBJECT * obj,long data FL_UNUSED_ARG)1567 vsl_cb( FL_OBJECT * obj,
1568         long        data  FL_UNUSED_ARG )
1569 {
1570     FLI_INPUT_SPEC *sp = obj->parent->spec;
1571 
1572     double val = fl_get_scrollbar_value( obj );
1573     int top = FL_nint( val * ( sp->lines - sp->screenlines ) ) + 1;
1574 
1575     sp->endrange = -1;          /* switch off selection */
1576     sp->drawtype = VSLIDER;
1577     fl_set_input_topline( sp->input, top );
1578 }
1579 
1580 
1581 /***************************************
1582  * Callback for the horizontal scrollbar of multi-line input objects
1583  ***************************************/
1584 
1585 static void
hsl_cb(FL_OBJECT * obj,long data FL_UNUSED_ARG)1586 hsl_cb( FL_OBJECT * obj,
1587         long        data  FL_UNUSED_ARG )
1588 {
1589     FLI_INPUT_SPEC *sp = obj->parent->spec;
1590     double val = fl_get_scrollbar_value( obj );
1591     int xoff = val * ( sp->max_pixels - sp->w ) + 0.1;
1592 
1593     sp->drawtype = HSLIDER;
1594     fl_set_input_xoffset( sp->input, xoff );
1595 }
1596 
1597 
1598 /***************************************
1599 * Pre- and post- handlers
1600  ***************************************/
1601 
1602 static int
input_pre(FL_OBJECT * obj,int ev,FL_Coord mx,FL_Coord my,int key,void * xev)1603 input_pre( FL_OBJECT * obj,
1604            int         ev,
1605            FL_Coord    mx,
1606            FL_Coord    my,
1607            int         key,
1608            void      * xev )
1609 {
1610     FL_OBJECT *ext = obj->parent;
1611 
1612     return ( ext && ext->prehandle ) ?
1613            ext->prehandle( ext, ev, mx, my, key, xev ) : 0;
1614 }
1615 
1616 
1617 /***************************************
1618  ***************************************/
1619 
1620 static int
input_post(FL_OBJECT * obj,int ev,FL_Coord mx,FL_Coord my,int key,void * xev)1621 input_post( FL_OBJECT * obj,
1622             int         ev,
1623             FL_Coord    mx,
1624             FL_Coord    my,
1625             int         key,
1626             void      * xev )
1627 {
1628     FL_OBJECT *ext = obj->parent;
1629 
1630     return ( ext && ext->posthandle ) ?
1631            ext->posthandle( ext, ev, mx, my, key, xev ) : 0;
1632 }
1633 
1634 
1635 /***************************************
1636  ***************************************/
1637 
1638 static void
input_cb(FL_OBJECT * obj,long data FL_UNUSED_ARG)1639 input_cb( FL_OBJECT * obj,
1640           long        data  FL_UNUSED_ARG )
1641 {
1642     obj->parent->returned = obj->returned;
1643 }
1644 
1645 
1646 /***************************************
1647  * Creates an input object
1648  ***************************************/
1649 
1650 FL_OBJECT *
fl_create_input(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)1651 fl_create_input( int          type,
1652                  FL_Coord     x,
1653                  FL_Coord     y,
1654                  FL_Coord     w,
1655                  FL_Coord     h,
1656                  const char * label )
1657 {
1658     FL_OBJECT *obj;
1659     FLI_INPUT_SPEC *sp;
1660 
1661     set_default_keymap( 0 );
1662 
1663     obj = fl_make_object( FL_INPUT, type, x, y, w, h, label, handle_input );
1664     obj->boxtype    = FL_INPUT_BOXTYPE;
1665     obj->col1       = FL_INPUT_COL1;
1666     obj->col2       = FL_INPUT_COL2;
1667     obj->align      = FL_INPUT_ALIGN;
1668     obj->lcol       = FL_INPUT_LCOL;
1669     obj->lsize      = fli_cntl.inputFontSize ?
1670                       fli_cntl.inputFontSize : FL_DEFAULT_SIZE;
1671     obj->set_return = fl_set_input_return;
1672 
1673     fl_set_object_prehandler( obj, input_pre );
1674     fl_set_object_posthandler( obj, input_post );
1675 
1676     obj->wantkey       = obj->type == FL_MULTILINE_INPUT ?
1677                          FL_KEY_ALL : FL_KEY_NORMAL;
1678     obj->want_update   = 1;
1679     obj->input         = 1;
1680     obj->click_timeout = FL_CLICK_TIMEOUT;
1681     obj->spec = sp     = fl_calloc( 1, sizeof *sp );
1682 
1683     sp->textcol        = FL_INPUT_TCOL;
1684     sp->curscol        = FL_INPUT_CCOL;
1685     sp->position       = -1;
1686     sp->endrange       = -1;
1687     sp->size           = 8;
1688     sp->lines          = sp->ypos = 1;
1689     sp->str            = fl_malloc( sp->size );
1690     *sp->str           = '\0';
1691     sp->cursor_visible = 1;
1692 
1693     switch ( obj->type )
1694     {
1695         case FL_DATE_INPUT :
1696             sp->maxchars = 10;
1697             break;
1698 
1699         case FL_SECRET_INPUT :
1700             sp->maxchars = 16;
1701             break;
1702 
1703         default :
1704             sp->maxchars = 0;
1705     }
1706 
1707     sp->dummy      = obj;
1708     sp->input      = obj;
1709     sp->field_char = ' ';
1710 
1711     if ( obj->type == FL_INT_INPUT )
1712         sp->validate = int_validator;
1713     else if ( obj->type == FL_FLOAT_INPUT )
1714         sp->validate = float_validator;
1715     else if ( obj->type == FL_DATE_INPUT )
1716     {
1717         fl_set_input_format( obj, FL_INPUT_MMDD, '/' );
1718         sp->validate = date_validator;
1719     }
1720 
1721     obj->how_return = FL_RETURN_END_CHANGED;
1722     fl_set_object_dblbuffer( obj, type != FL_HIDDEN_INPUT );
1723 
1724     return obj;
1725 }
1726 
1727 
1728 /***************************************
1729  ***************************************/
1730 
1731 static int
fake_handle(FL_OBJECT * obj,int event,FL_Coord mx FL_UNUSED_ARG,FL_Coord my FL_UNUSED_ARG,int key FL_UNUSED_ARG,void * ev FL_UNUSED_ARG)1732 fake_handle( FL_OBJECT * obj,
1733              int         event,
1734              FL_Coord    mx   FL_UNUSED_ARG,
1735              FL_Coord    my   FL_UNUSED_ARG,
1736              int         key  FL_UNUSED_ARG,
1737              void      * ev   FL_UNUSED_ARG )
1738 {
1739     FLI_INPUT_SPEC *sp = obj->spec;
1740 
1741     switch ( event )
1742     {
1743         case FL_ATTRIB:
1744             if ( sp->dummy != obj ) /* this can only happen with fdesign */
1745                 sp->dummy = obj;
1746             copy_attributes( sp->input, sp->dummy );
1747             /* fall through */
1748 
1749         case FL_DRAW:
1750         case FL_DRAWLABEL:
1751             check_scrollbar_size( obj );
1752             break;
1753     }
1754 
1755     return 0;
1756 }
1757 
1758 
1759 /***************************************
1760  * Adds an input object
1761  ***************************************/
1762 
1763 FL_OBJECT *
fl_add_input(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * label)1764 fl_add_input( int          type,
1765               FL_Coord     x,
1766               FL_Coord     y,
1767               FL_Coord     w,
1768               FL_Coord     h,
1769               const char * label )
1770 {
1771     FL_OBJECT *obj;
1772     FLI_INPUT_SPEC *sp;
1773     int oldu = fl_get_coordunit( );
1774 
1775     obj = fl_create_input( type, x, y, w, h, label );
1776     sp = obj->spec;
1777 
1778     fl_set_coordunit( FL_COORD_PIXEL );
1779 
1780     x = obj->x;
1781     y = obj->y;
1782     w = obj->w;
1783     h = obj->h;
1784 
1785     if ( obj->type == FL_MULTILINE_INPUT )
1786     {
1787         fl_set_object_label( obj, NULL );
1788         sp->dummy = fl_create_box( FL_NO_BOX, x, y, w, h, label );
1789         sp->dummy->objclass   = FL_INPUT;
1790         sp->dummy->type       = FL_MULTILINE_INPUT;
1791         copy_attributes( sp->dummy, obj );
1792         sp->dummy->handle     = fake_handle;
1793         sp->dummy->spec       = sp;
1794         sp->dummy->set_return = fl_set_input_return;
1795 
1796         fl_add_child( sp->dummy, obj );
1797 
1798         sp->hh_def = sp->vw_def = fli_get_default_scrollbarsize( obj );
1799         sp->h_pref = sp->v_pref = FL_AUTO;
1800         sp->vscroll = fl_create_scrollbar( fli_context->vscb,
1801                                            x + w - sp->vw_def,
1802                                            y, sp->vw_def, h, NULL );
1803         fl_set_scrollbar_value( sp->vscroll, 0.0 );
1804         fl_set_object_callback( sp->vscroll, vsl_cb, 0 );
1805         fl_set_object_resize( sp->vscroll, FL_RESIZE_NONE );
1806         fl_add_child( sp->dummy, sp->vscroll );
1807 
1808         sp->hscroll = fl_create_scrollbar( fli_context->hscb, x,
1809                                            y + h - sp->hh_def,
1810                                            w, sp->hh_def, NULL );
1811         fl_set_scrollbar_value( sp->hscroll, 0.0 );
1812         fl_set_object_callback( sp->hscroll, hsl_cb, 0 );
1813         fl_set_object_resize( sp->hscroll, FL_RESIZE_NONE );
1814         fl_add_child( sp->dummy, sp->hscroll );
1815 
1816         fl_set_object_callback( sp->input, input_cb, 0 );
1817         fl_set_object_return( sp->dummy, FL_RETURN_END_CHANGED );
1818     }
1819 
1820     fl_add_object( fl_current_form, sp->dummy );
1821 
1822     fl_set_coordunit( oldu );
1823 
1824     /* Set default return policy for the new object */
1825 
1826     fl_set_object_return( obj, FL_RETURN_END_CHANGED );
1827 
1828     return sp->dummy;
1829 }
1830 
1831 
1832 /***************************************
1833  * Sets the input string. Only printable character and, for multi-line
1834  * inputs new-lines are accepted.
1835  ***************************************/
1836 
1837 #define IS_VALID_INPUT_CHAR( c )   \
1838        isprint( ( unsigned char ) ( c ) )    \
1839     || ( obj->type == FL_MULTILINE_INPUT && ( c ) == '\n' )
1840 
1841 void
fl_set_input(FL_OBJECT * obj,const char * str)1842 fl_set_input( FL_OBJECT  * obj,
1843               const char * str )
1844 {
1845     FLI_INPUT_SPEC *sp = obj->spec;
1846     int len;
1847     char *p;
1848     const char *q;
1849 
1850     if ( ! str )
1851         str = "";
1852 
1853     for ( len = 0, q = str; *q; ++q )
1854         if ( IS_VALID_INPUT_CHAR( *q ) )
1855             ++len;
1856 
1857     if ( sp->size < len + 1 )
1858     {
1859         sp->size = len + 9;
1860         sp->str = fl_realloc( sp->str, sp->size );
1861     }
1862 
1863     for ( p = sp->str, q = str; *q; ++q )
1864         if ( IS_VALID_INPUT_CHAR( *q ) )
1865             *p++ = *q;
1866     *p = '\0';
1867 
1868     /* Set position of cursor in string to the end (if object doesn't has
1869        focus must be negative of length of strig minus one) */
1870 
1871     if ( sp->position >= 0 )
1872         sp->position = len;
1873     else
1874         sp->position = - len - 1;
1875 
1876     sp->endrange = -1;
1877 
1878     sp->lines = fl_get_input_numberoflines( obj );
1879     fl_get_input_cursorpos( obj, &sp->xpos, &sp->ypos );
1880 
1881     /* Get max string width - it's possible that fl_set_input() is used before
1882        the form is show, draw_object is a no-op, thus we end up with a wrong
1883        string size */
1884 
1885     fl_get_string_dimension( obj->lstyle, obj->lsize,
1886                              sp->str, len, &sp->max_pixels, &len );
1887 
1888     if ( obj->form )
1889         fl_freeze_form( obj->form );
1890 
1891     check_scrollbar_size( obj );
1892     make_line_visible( obj, sp->ypos );
1893     fl_redraw_object( sp->input );
1894     sp->xoffset = 0;
1895     check_scrollbar_size( obj );
1896     if ( sp->v_on || sp->h_on )
1897         redraw_scrollbar( obj );
1898 
1899     if ( obj->form )
1900         fl_unfreeze_form( obj->form );
1901 }
1902 
1903 
1904 /***************************************
1905  * Sets the input string using a format string and an appropriate number
1906  * of (unspecified) arguments
1907  ***************************************/
1908 
1909 void
fl_set_input_f(FL_OBJECT * obj,const char * fmt,...)1910 fl_set_input_f( FL_OBJECT  * obj,
1911                 const char * fmt,
1912                 ... )
1913 {
1914     char *buf;
1915 
1916     EXPAND_FORMAT_STRING( buf, fmt );
1917     fl_set_input( obj, buf );
1918     fl_free( buf );
1919 }
1920 
1921 
1922 /***************************************
1923  * Sets the color of the input string
1924  ***************************************/
1925 
1926 void
fl_set_input_color(FL_OBJECT * obj,FL_COLOR textcol,FL_COLOR curscol)1927 fl_set_input_color( FL_OBJECT * obj,
1928                     FL_COLOR    textcol,
1929                     FL_COLOR    curscol )
1930 {
1931     FLI_INPUT_SPEC *sp = obj->spec;
1932 
1933     sp->textcol = textcol;
1934     sp->curscol = curscol;
1935     fl_redraw_object( sp->input );
1936 }
1937 
1938 
1939 /***************************************
1940  * Returns the color of the input string
1941  ***************************************/
1942 
1943 void
fl_get_input_color(FL_OBJECT * obj,FL_COLOR * textcol,FL_COLOR * curscol)1944 fl_get_input_color( FL_OBJECT * obj,
1945                     FL_COLOR  * textcol,
1946                     FL_COLOR  * curscol )
1947 {
1948     FLI_INPUT_SPEC *sp = obj->spec;
1949 
1950     *textcol = sp->textcol;
1951     *curscol = sp->curscol;
1952 }
1953 
1954 
1955 /***************************************
1956  ***************************************/
1957 
1958 int
fl_set_input_fieldchar(FL_OBJECT * obj,int fchar)1959 fl_set_input_fieldchar( FL_OBJECT * obj,
1960                         int         fchar )
1961 {
1962     FLI_INPUT_SPEC *sp = obj->spec;
1963     int ochar = sp->field_char;
1964 
1965     if ( obj->objclass != FL_INPUT )
1966     {
1967         M_err( "fl_set_input_fieldchar", "%s isn't an input object",
1968                obj ? obj->label : "null" );
1969         return 0;
1970     }
1971 
1972     sp->field_char = fchar;
1973     return ochar;
1974 }
1975 
1976 
1977 /***************************************
1978  * Returns a pointer to the text string
1979  ***************************************/
1980 
1981 const char *
fl_get_input(FL_OBJECT * obj)1982 fl_get_input( FL_OBJECT * obj )
1983 {
1984     return ( ( FLI_INPUT_SPEC * ) obj->spec )->str;
1985 }
1986 
1987 
1988 /***************************************
1989  * Sets under which conditions the object is to be returned to the
1990  * application. This function should be regarded as for internal use
1991  * only and fl_set_object_return() should be used instead (which then
1992  * will call this function).
1993  ***************************************/
1994 
1995 void
fl_set_input_return(FL_OBJECT * obj,unsigned int when)1996 fl_set_input_return( FL_OBJECT    * obj,
1997                      unsigned int   when )
1998 {
1999     FLI_INPUT_SPEC *sp = obj->spec;
2000 
2001     if ( when & FL_RETURN_END_CHANGED )
2002         when &= ~ ( FL_RETURN_END | FL_RETURN_CHANGED );
2003 
2004     obj->how_return = sp->input->how_return = when;
2005     fl_set_object_return( sp->vscroll, FL_RETURN_CHANGED );
2006     fl_set_object_return( sp->hscroll, FL_RETURN_CHANGED );
2007 }
2008 
2009 
2010 /***************************************
2011  ***************************************/
2012 
2013 void
fl_set_input_scroll(FL_OBJECT * obj,int yes)2014 fl_set_input_scroll( FL_OBJECT * obj,
2015                      int         yes )
2016 {
2017     ( ( FLI_INPUT_SPEC * ) obj->spec )->noscroll = ! yes;
2018 }
2019 
2020 
2021 /***************************************
2022  * Makes a part of an input string selected or deselected
2023  ***************************************/
2024 
2025 void
fl_set_input_selected_range(FL_OBJECT * obj,int begin,int end)2026 fl_set_input_selected_range( FL_OBJECT * obj,
2027                              int         begin,
2028                              int         end )
2029 {
2030     FLI_INPUT_SPEC *sp = obj->spec;
2031     int len;
2032 
2033     if ( obj->type == FL_HIDDEN_INPUT )
2034         return;
2035 
2036     len = strlen( sp->str );
2037 
2038     if ( begin < 0 )
2039         sp->beginrange = 0;
2040     else if ( begin > len )
2041         sp->beginrange = len;
2042     else
2043         sp->beginrange = begin;
2044 
2045     if ( end < 0 )
2046         sp->endrange = -1;
2047     else if ( end > len )
2048         sp->endrange = len;
2049     else
2050         sp->endrange = end;
2051 
2052     /* move cursor to the head */
2053 
2054     sp->position = sp->beginrange;
2055     fl_redraw_object( sp->input );
2056 }
2057 
2058 
2059 /***************************************
2060  ***************************************/
2061 
2062 const char *
fl_get_input_selected_range(FL_OBJECT * obj,int * begin,int * end)2063 fl_get_input_selected_range( FL_OBJECT * obj,
2064                              int       * begin,
2065                              int       * end )
2066 {
2067     FLI_INPUT_SPEC *sp = obj->spec;
2068     static char *selbuf;
2069     static int nselbuf;
2070     int n;
2071 
2072     n = sp->endrange - sp->beginrange;
2073 
2074     if ( n < 1 )
2075     {
2076         if ( begin )
2077             *begin = -1;
2078         if ( end )
2079             *end = -1;
2080         return NULL;
2081     }
2082 
2083     if ( begin )
2084         *begin = sp->beginrange;
2085 
2086     if ( end )
2087         *end = sp->endrange;
2088 
2089     if ( n != nselbuf )
2090     {
2091         selbuf = fl_realloc( selbuf, n + 1 );
2092         nselbuf = n;
2093     }
2094 
2095     fli_sstrcpy( selbuf, sp->str + sp->beginrange, n );
2096 
2097     return selbuf;
2098 }
2099 
2100 
2101 /***************************************
2102  * Selects the current input programmatically without moving
2103  * the cursor
2104  ***************************************/
2105 
2106 void
fl_set_input_selected(FL_OBJECT * obj,int yes)2107 fl_set_input_selected( FL_OBJECT * obj,
2108                        int         yes )
2109 {
2110     FLI_INPUT_SPEC *sp = obj->spec;
2111 
2112     if ( obj->type == FL_HIDDEN_INPUT )
2113         return;
2114 
2115     if ( yes )
2116     {
2117         sp->position = sp->endrange = strlen( sp->str );
2118         sp->beginrange = 0;
2119     }
2120     else
2121         sp->endrange = -1;
2122 
2123     fl_redraw_object( sp->input );
2124 }
2125 
2126 
2127 /***************************************
2128  * Given an (x,y) location returns the string position in chars
2129  ***************************************/
2130 
2131 static int
xytopos(FLI_INPUT_SPEC * sp,int xpos,int ypos)2132 xytopos( FLI_INPUT_SPEC * sp,
2133          int              xpos,
2134          int              ypos )
2135 {
2136     int newp = 0;
2137     char *s = sp->str,
2138          *se = s + strlen( s );
2139 
2140     if ( ypos < 1 )
2141         ypos = 1;
2142     else if ( ypos > sp->lines )
2143         ypos = sp->lines;
2144 
2145     if ( xpos < 0 )
2146         xpos = 0;
2147 
2148     sp->ypos = 1;
2149     while ( sp->ypos < ypos && ( s = strchr( s, '\n' ) ) )
2150     {
2151         sp->ypos++;
2152         s++;
2153         newp = s - sp->str;
2154     }
2155 
2156     s = sp->str + newp;
2157     for ( sp->xpos = 0; sp->xpos < xpos && s < se; sp->xpos++, newp++ )
2158         if (  *++s == '\n' )
2159             break;
2160 
2161     return sp->position = newp;
2162 }
2163 
2164 
2165 /***************************************
2166  * Move cursor within the input field, cursor position is measured in chars
2167  ***************************************/
2168 
2169 void
fl_set_input_cursorpos(FL_OBJECT * obj,int xpos,int ypos)2170 fl_set_input_cursorpos( FL_OBJECT * obj,
2171                         int         xpos,
2172                         int         ypos )
2173 {
2174     FLI_INPUT_SPEC *sp = obj->spec;
2175     int oldp = sp->position;
2176 
2177     if ( obj->type == FL_HIDDEN_INPUT )
2178         return;
2179 
2180     if ( oldp != xytopos( sp, xpos, ypos ) )
2181     {
2182         fl_freeze_form( obj->form );
2183         make_line_visible( obj, sp->ypos );
2184         make_char_visible( obj, sp->xpos );
2185         fl_redraw_object( obj );
2186         fl_unfreeze_form( obj->form );
2187     }
2188 }
2189 
2190 
2191 /***************************************
2192  ***************************************/
2193 
2194 int
fl_get_input_cursorpos(FL_OBJECT * obj,int * x,int * y)2195 fl_get_input_cursorpos( FL_OBJECT * obj,
2196                         int       * x,
2197                         int       * y )
2198 {
2199     FLI_INPUT_SPEC *sp = obj->spec;
2200     char *s = sp->str;
2201     int cnt = 0;
2202 
2203     if ( ! obj->focus )
2204         return sp->position = *x = -1;
2205 
2206     *y = 1;
2207     *x = 0;
2208 
2209     for ( ; s && *s && cnt < sp->position; s++, cnt++ )
2210         if ( *s == '\n' )
2211         {
2212             *y += 1;
2213             *x = 0;
2214         }
2215         else
2216             *x += 1;
2217 
2218     return sp->position;
2219 }
2220 
2221 
2222 /***************************************
2223  * Reverts to the default keymap for edit keys
2224  ***************************************/
2225 
2226 void
fl_set_default_editkeymap(void)2227 fl_set_default_editkeymap( void )
2228 {
2229     set_default_keymap( 1 );
2230 }
2231 
2232 
2233 #define Ctrl( c ) \
2234 ( ( tolower( ( unsigned char ) c ) - 'a' + 1 ) | FL_CONTROL_MASK )
2235 #define Meta( c ) ( tolower( ( unsigned char ) c ) | FL_ALT_MASK )
2236 
2237 /***************************************
2238  ***************************************/
2239 
2240 static void
set_default_keymap(int force)2241 set_default_keymap( int force )
2242 {
2243     static int initialized = 0;
2244 
2245     if ( ! force && initialized )
2246         return;
2247 
2248     initialized = 1;
2249 
2250     /* Emacs defaults */
2251 
2252     kmap.moveto_next_char = Ctrl( 'f' );
2253     kmap.moveto_prev_char = Ctrl( 'b' );
2254     kmap.moveto_prev_word = Meta( 'b' );
2255     kmap.moveto_next_word = Meta( 'f' );
2256 
2257     kmap.moveto_bol       = Ctrl( 'a' );
2258     kmap.moveto_eol       = Ctrl( 'e' );
2259 
2260     kmap.moveto_next_line = Ctrl( 'n' );
2261     kmap.moveto_prev_line = Ctrl( 'p' );
2262 
2263     kmap.moveto_bof       = Meta( '<' );
2264     kmap.moveto_eof       = Meta( '>' );
2265 
2266     kmap.del_next_char    = Ctrl( 'd' );
2267     kmap.del_prev_char    = Ctrl( 'h' );
2268     kmap.del_next_word    = Meta( 'd' );
2269     kmap.del_prev_word    = Meta( '\b' );
2270 
2271     kmap.del_to_eol       = Ctrl( 'k' );
2272     kmap.del_to_bol       = Meta( 'k' );
2273 
2274     kmap.backspace        = '\b';
2275     kmap.transpose        = Ctrl( 't' );
2276     kmap.paste            = Ctrl( 'y' );
2277     kmap.clear_field      = Ctrl( 'u' );
2278 }
2279 
2280 
2281 /***************************************
2282  ***************************************/
2283 
2284 void
fl_set_input_maxchars(FL_OBJECT * obj,int maxchars)2285 fl_set_input_maxchars( FL_OBJECT * obj,
2286                        int         maxchars )
2287 {
2288     ( ( FLI_INPUT_SPEC * ) obj->spec )->maxchars = maxchars;
2289 }
2290 
2291 
2292 /***************************************
2293  ***************************************/
2294 
2295 FL_INPUT_VALIDATOR
fl_set_input_filter(FL_OBJECT * obj,FL_INPUT_VALIDATOR validate)2296 fl_set_input_filter( FL_OBJECT          * obj,
2297                      FL_INPUT_VALIDATOR   validate )
2298 {
2299     FLI_INPUT_SPEC *sp = obj->spec;
2300     FL_INPUT_VALIDATOR old = sp->validate;
2301 
2302     sp->validate = validate;
2303     return old;
2304 }
2305 
2306 
2307 /***************************************
2308  ***************************************/
2309 
2310 void
fl_set_input_format(FL_OBJECT * obj,int fmt,int sep)2311 fl_set_input_format( FL_OBJECT * obj,
2312                      int         fmt,
2313                      int         sep )
2314 {
2315     FLI_INPUT_SPEC *sp = obj->spec;
2316 
2317     if (    ! isprint( ( unsigned char ) sep )
2318          || isdigit( ( unsigned char ) sep ) )
2319         sep = '/';
2320     sp->attrib1 = fmt;
2321     sp->attrib2 = sep;
2322 }
2323 
2324 
2325 /***************************************
2326  ***************************************/
2327 
2328 void
fl_get_input_format(FL_OBJECT * obj,int * fmt,int * sep)2329 fl_get_input_format( FL_OBJECT * obj,
2330                      int       * fmt,
2331                      int       * sep )
2332 {
2333     FLI_INPUT_SPEC *sp = obj->spec;
2334 
2335     *fmt = sp->attrib1;
2336     *sep = sp->attrib2;
2337 }
2338 
2339 
2340 /***************************************
2341  ***************************************/
2342 
2343 int
fl_validate_input(FL_OBJECT * obj)2344 fl_validate_input( FL_OBJECT *obj )
2345 {
2346     FLI_INPUT_SPEC *sp = obj->spec;
2347 
2348     return (    ! sp->validate
2349              || sp->validate( obj, sp->str, sp->str, 0 ) == FL_VALID ) ?
2350            FL_VALID : FL_INVALID;
2351 }
2352 
2353 
2354 /***************************************
2355  * Validator for date
2356  ***************************************/
2357 
2358 #define IS_LEAP_YEAR( y )   \
2359     ( ( y ) % 4 == 0 && ( ( y ) % 100 != 0 || ( y ) % 400 == 0 ) )
2360 
2361 static int
date_validator(FL_OBJECT * obj,const char * oldstr FL_UNUSED_ARG,const char * newstr,int newc)2362 date_validator( FL_OBJECT  * obj,
2363                 const char * oldstr  FL_UNUSED_ARG,
2364                 const char * newstr,
2365                 int          newc )
2366 {
2367     char *val,
2368          *s,
2369          sepsep[ 3 ];
2370     char ssep[ 2 ] = "";
2371     int i,
2372         len,
2373         ival[ ] = { 1, 1, 1 };
2374     int fmt,
2375         sep;
2376     int invalid = FL_RINGBELL | FL_INVALID;
2377     int m,
2378         d;
2379 
2380     /* Consider empty valid */
2381 
2382     if ( ( len = strlen( newstr ) ) == 0 )
2383         return FL_VALID;
2384 
2385     fl_get_input_format( obj, &fmt, &sep );
2386 
2387     *ssep = sep;
2388     strcat( strcpy( sepsep, ssep ), ssep );
2389 
2390 
2391     /* Allow only separator or digit, but no separator at the very start or
2392        two separators in a row */
2393 
2394     if (    (    newc != sep
2395               && newc != '\0'
2396               && ! isdigit( ( unsigned char ) newc ) )
2397          || *newstr == sep || strstr( newstr, sepsep ) )
2398         return invalid;
2399 
2400     s = fl_strdup( newstr );
2401 
2402     /* Split up the date string at the separators and convert to ints and
2403        check that there aren't more than 2 digits for day and month and 4
2404        for the year */
2405 
2406     for ( i = 0, val = strtok( s, ssep ); i < 3 && val;
2407           val = strtok( NULL, ssep ) )
2408     {
2409         if ( ( i < 2 && strlen( val ) > 2 ) || ( i == 2 && strlen( val ) > 4 ) )
2410              return invalid;
2411 
2412         ival[ i++ ] = atoi( val );
2413 
2414         if ( i <= 2 && strlen( val ) == 2 && ival[ i - 1 ] == 0 )
2415             return invalid;
2416     }
2417 
2418     fl_free( s );
2419 
2420     /* Allow one or more '0' at the start of the day pr month (but not as
2421        the only digit, i.e. when the new character is the separator) */
2422 
2423     if (    newc == sep
2424          && (    ( i == 1 && ival[ 0 ] == 0 )
2425               || ( i == 2 && ival[ 1 ] == 0 ) ) )
2426         return invalid;
2427 
2428     /* Don't allow another separator when we already got 3 items */
2429 
2430     if ( i == 3 && newstr[ len - 1 ] == sep )
2431        return invalid;
2432 
2433     /* Always require 3 items when the user tries to leave the field
2434        and make sure that day or month aren't 0 */
2435 
2436     if (    newc == '\0'
2437          && ( i != 3 || ival[ 0 ] == 0 || ival[ 1 ] == 0 ) )
2438         return invalid;
2439 
2440     m = fmt == FL_INPUT_MMDD ? 0 : 1;
2441     d = ! m;
2442 
2443     /* Basic check for upper limits of day and month */
2444 
2445     if ( ival[ m ] > 12 || ival[ d ] > 31 )
2446         return invalid;
2447 
2448     /* More precise check for days in month */
2449 
2450     if (    ( ival[ d ] > 30 && ( ival[ m ] % 2 ) == 0 && ival[ m ] < 8 )
2451          || ( ival[ d ] > 30 && ( ival[ m ] % 2 ) != 0 && ival[ m ] > 8 ) )
2452         return invalid;
2453 
2454     /* Take care: check for leap year can only be done when leaving */
2455 
2456     if (    ival[ m ] == 2
2457          && (    ival[ d ] > 29
2458               || (    i == 3
2459                    && newc == 0
2460                    && ival[ d ] > 28
2461                    && ! IS_LEAP_YEAR( ival[ 2 ] ) ) ) )
2462         return invalid;
2463 
2464     return FL_VALID;
2465 }
2466 
2467 
2468 /***************************************
2469  * Validator for integer fields
2470  ***************************************/
2471 
2472 static int
int_validator(FL_OBJECT * obj FL_UNUSED_ARG,const char * oldstr FL_UNUSED_ARG,const char * str,int newc)2473 int_validator( FL_OBJECT  * obj     FL_UNUSED_ARG,
2474                const char * oldstr  FL_UNUSED_ARG,
2475                const char * str,
2476                int          newc )
2477 {
2478     char *eptr = NULL;
2479     long dummy;
2480 
2481     /* The empty string is considered to be valid */
2482 
2483     if ( ! *str )
2484         return FL_VALID;
2485 
2486     /* If there was a new character and all we got is a '+' or '-' this is ok */
2487 
2488     if ( ! str[ 1 ] && ( newc == '+' || newc == '-' ) )
2489         return FL_VALID;
2490 
2491     dummy = strtol( str, &eptr, 10 );
2492 
2493     if (    ( ( dummy == LONG_MAX || dummy == LONG_MIN ) && errno == ERANGE )
2494          || *eptr )
2495         return FL_INVALID | FL_RINGBELL;
2496 
2497     return FL_VALID;
2498 }
2499 
2500 
2501 /***************************************
2502  * Validator for floating point fields
2503  ***************************************/
2504 
2505 static int
float_validator(FL_OBJECT * obj FL_UNUSED_ARG,const char * oldstr FL_UNUSED_ARG,const char * str,int newc)2506 float_validator( FL_OBJECT  * obj     FL_UNUSED_ARG,
2507                  const char * oldstr  FL_UNUSED_ARG,
2508                  const char * str,
2509                  int          newc )
2510 {
2511     char *eptr = NULL;
2512     size_t len;
2513     double dummy;
2514 
2515     /* The empty string is considered valid */
2516 
2517     if ( ! *str )
2518         return FL_VALID;
2519 
2520     /* Try strtod() an the string*/
2521 
2522     dummy = strtod( str, &eptr );
2523 
2524     /* If it reports no problem were done */
2525 
2526     if (    ! ( ( dummy == HUGE_VAL || dummy == -HUGE_VAL ) && errno == ERANGE )
2527          && ! *eptr )
2528         return FL_VALID;
2529 
2530     /* Otherwise, if there's no new character the input must be invalid */
2531 
2532     if ( ! newc )
2533         return FL_INVALID | FL_RINGBELL;
2534 
2535     /* Now we handle cases that strtod() flagged as bad but which we need to
2536        accept while editing is still underway. If there's only a single char
2537        it should be a dot or a sign */
2538 
2539     len = strlen( str );
2540 
2541     if ( len == 1 )
2542         return ( newc == '+' || newc == '-' || newc == '.' ) ?
2543             FL_VALID : FL_INVALID | FL_RINGBELL;
2544 
2545     /* If there are two characters accept a sequence of a sign and a decimal
2546        point or a number followed by 'e' or 'E' */
2547 
2548     if ( len == 2 )
2549         return (    ! strcmp( str, "+." )
2550                  || ! strcmp( str, "-." )
2551                  || *eptr == 'e'
2552                  || *eptr == 'E' ) ?
2553                FL_VALID : ( FL_INVALID | FL_RINGBELL );
2554 
2555     /* Accept an 'e' or 'E' in the last position, or, if it's in the second last
2556        position, when it's followed by a sign - but only if it's the first 'e'
2557        or 'E' in the string */
2558 
2559     if ( ( *eptr == 'e' || *eptr == 'E' )
2560          && strchr( str, *eptr ) == eptr
2561          && (    eptr == str + len - 1
2562               || (    eptr == str + len - 2
2563                    && ( eptr[ 1 ] == '+' || eptr[ 1 ] == '-' ) ) ) )
2564          return FL_VALID;
2565 
2566     return FL_INVALID | FL_RINGBELL;
2567 }
2568 
2569 
2570 /***************************************
2571  ***************************************/
2572 
2573 void
fl_set_input_xoffset(FL_OBJECT * obj,int xoff)2574 fl_set_input_xoffset( FL_OBJECT * obj,
2575                       int         xoff )
2576 {
2577     FLI_INPUT_SPEC *sp = obj->spec;
2578 
2579     if ( sp->xoffset != xoff )
2580     {
2581         sp->xoffset = xoff;
2582         if ( sp->drawtype != HSLIDER )
2583         {
2584             check_scrollbar_size( obj );
2585             redraw_scrollbar( obj );
2586         }
2587         sp->drawtype = COMPLETE;
2588         fl_redraw_object( sp->input );
2589     }
2590 }
2591 
2592 
2593 /***************************************
2594  ***************************************/
2595 
2596 int
fl_get_input_xoffset(FL_OBJECT * obj)2597 fl_get_input_xoffset( FL_OBJECT * obj )
2598 {
2599     return ( ( FLI_INPUT_SPEC * ) obj->spec )->xoffset;
2600 }
2601 
2602 
2603 /***************************************
2604  * Corrects, if necessary, the value of 'top' (the topmost line to be
2605  * shown in a FL_MULTILINE_INPUT object, count is starting at 1) so
2606  * that the value is at least 1 and, if there are less lines below
2607  * 'top' then fit into the input field, adjusts 'top' that the
2608  * complete field is used (i.e. there are no empty lines at the end).
2609  ***************************************/
2610 
2611 static void
correct_topline(FLI_INPUT_SPEC * sp,int * top)2612 correct_topline( FLI_INPUT_SPEC * sp,
2613                  int            * top )
2614 {
2615     if ( sp->lines > sp->screenlines )
2616     {
2617         if ( sp->lines < *top + sp->screenlines - 1 )
2618             *top = sp->lines - sp->screenlines + 1;
2619         if ( *top < 1 )
2620             *top = 1;
2621     }
2622     else
2623         *top = 1;
2624 }
2625 
2626 
2627 /***************************************
2628  ***************************************/
2629 
2630 int
fl_get_input_numberoflines(FL_OBJECT * obj)2631 fl_get_input_numberoflines( FL_OBJECT * obj )
2632 {
2633     FLI_INPUT_SPEC *sp = obj->spec;
2634     int count;
2635     const char *s = sp->str;
2636 
2637     if ( ! s )
2638         return sp->lines = 0;
2639 
2640     for ( count = 1; *s; s++ )
2641         if ( *s == '\n' )
2642             count++;
2643 
2644     return sp->lines = count;
2645 }
2646 
2647 
2648 /***************************************
2649  * Makes the requested line 'top' (starting at 1) the topmost line
2650  * shown in a FL_MULTILINE_INPUT object.
2651  ***************************************/
2652 
2653 void
fl_set_input_topline(FL_OBJECT * obj,int top)2654 fl_set_input_topline( FL_OBJECT * obj,
2655                       int         top )
2656 {
2657     FLI_INPUT_SPEC *sp = obj->spec;
2658 
2659     if ( sp->input->type != FL_MULTILINE_INPUT )
2660         return;
2661 
2662     correct_topline( sp, &top );
2663 
2664     if ( sp->topline == top )
2665         return;
2666 
2667     /* Make sure the cursor remains in a visible line */
2668 
2669     if ( sp->ypos < top || sp->ypos >= top + sp->screenlines )
2670     {
2671         if ( sp->ypos < top )
2672             xytopos( sp, sp->xpos, top );
2673         else
2674             xytopos( sp, sp->xpos, top + sp->screenlines - 1 );
2675         make_char_visible( obj, sp->xpos );
2676     }
2677 
2678     sp->topline = top;
2679     if ( sp->drawtype != VSLIDER )
2680     {
2681         check_scrollbar_size( obj );
2682         redraw_scrollbar( obj );
2683     }
2684 
2685     sp->drawtype = COMPLETE;
2686     sp->yoffset = ( sp->topline - 1 ) * sp->charh;
2687     fl_redraw_object( sp->input );
2688 }
2689 
2690 
2691 /***************************************
2692  * Line number 'n' is numbered from 1.
2693  ***************************************/
2694 
2695 static int
make_line_visible(FL_OBJECT * obj,int ypos)2696 make_line_visible( FL_OBJECT * obj,
2697                    int         ypos )
2698 {
2699     FLI_INPUT_SPEC *sp = obj->spec;
2700     int oldtop = sp->topline;
2701 
2702     if ( obj->type != FL_MULTILINE_INPUT )
2703         return 0;
2704 
2705     if ( ypos < sp->topline )
2706         fl_set_input_topline( obj, ypos );
2707     else if ( ypos > sp->topline + sp->screenlines - 1 )
2708         fl_set_input_topline( obj, ypos - sp->screenlines + 1 );
2709     else if ( sp->lines < sp->screenlines )
2710         fl_set_input_topline( obj, 1 );
2711 
2712     return oldtop != sp->topline;
2713 }
2714 
2715 
2716 /***************************************
2717  ***************************************/
2718 
2719 static int
make_char_visible(FL_OBJECT * obj,int xpos)2720 make_char_visible( FL_OBJECT * obj,
2721                    int         xpos )
2722 {
2723     FLI_INPUT_SPEC *sp = obj->spec;
2724     int start_of_line = sp->position;
2725     int oldxoffset = sp->xoffset;
2726     int tmp;
2727 
2728     if ( xpos < 0 )
2729         return 0;
2730 
2731     while ( start_of_line > 0 && sp->str[ start_of_line - 1 ] != '\n' )
2732         start_of_line--;
2733 
2734     tmp = get_substring_width( obj, start_of_line, start_of_line + xpos );
2735 
2736     if ( tmp < sp->xoffset )
2737         sp->xoffset = tmp;
2738     else if ( tmp - sp->xoffset > sp->w )
2739         sp->xoffset = tmp - sp->w;
2740 
2741     if ( sp->xoffset != oldxoffset )
2742     {
2743         check_scrollbar_size( obj );
2744         redraw_scrollbar( obj );
2745         fl_redraw_object( sp->input );
2746         return 1;
2747     }
2748 
2749     return 0;
2750 }
2751 
2752 
2753 /***************************************
2754  ***************************************/
2755 
2756 void
fl_set_input_vscrollbar(FL_OBJECT * obj,int pref)2757 fl_set_input_vscrollbar( FL_OBJECT * obj,
2758                          int         pref )
2759 {
2760     FLI_INPUT_SPEC *sp = obj->spec;
2761 
2762     if ( sp->v_pref != pref )
2763     {
2764         sp->v_pref = pref;
2765         check_scrollbar_size( obj );
2766         redraw_scrollbar( obj );
2767         fl_redraw_object( sp->input );
2768     }
2769 }
2770 
2771 
2772 /***************************************
2773  ***************************************/
2774 
2775 void
fl_set_input_hscrollbar(FL_OBJECT * obj,int pref)2776 fl_set_input_hscrollbar( FL_OBJECT * obj,
2777                          int         pref )
2778 {
2779     FLI_INPUT_SPEC *sp = obj->spec;
2780 
2781     if ( sp->h_pref != pref )
2782     {
2783         sp->h_pref = pref;
2784         check_scrollbar_size( obj );
2785         redraw_scrollbar( obj );
2786         fl_redraw_object( sp->input );
2787     }
2788 }
2789 
2790 
2791 /***************************************
2792  ***************************************/
2793 
2794 void
fl_set_input_scrollbarsize(FL_OBJECT * obj,int hh,int vw)2795 fl_set_input_scrollbarsize( FL_OBJECT * obj,
2796                             int         hh,
2797                             int         vw )
2798 {
2799     FLI_INPUT_SPEC *sp = obj->spec;
2800 
2801     if ( sp->hh_def != hh || sp->vw_def != vw )
2802     {
2803         sp->hh_def = hh;
2804         sp->vw_def = vw;
2805         check_scrollbar_size( obj );
2806         redraw_scrollbar( obj );
2807         fl_redraw_object( sp->input );
2808     }
2809 }
2810 
2811 
2812 /***************************************
2813  ***************************************/
2814 
2815 void
fl_get_input_scrollbarsize(FL_OBJECT * obj,int * hh,int * vw)2816 fl_get_input_scrollbarsize( FL_OBJECT * obj,
2817                             int       * hh,
2818                             int       * vw )
2819 {
2820     FLI_INPUT_SPEC *sp = obj->spec;
2821 
2822     *hh = sp->hh_def;
2823     *vw = sp->vw_def;
2824 }
2825 
2826 
2827 /***************************************
2828  ***************************************/
2829 
2830 int
fl_get_input_topline(FL_OBJECT * obj)2831 fl_get_input_topline( FL_OBJECT * obj )
2832 {
2833     FLI_INPUT_SPEC *sp = obj->spec;
2834 
2835     if ( sp->input->type != FL_MULTILINE_INPUT )
2836         return 1;
2837 
2838     return sp->topline;
2839 }
2840 
2841 
2842 /***************************************
2843  ***************************************/
2844 
2845 int
fl_get_input_screenlines(FL_OBJECT * obj)2846 fl_get_input_screenlines( FL_OBJECT * obj )
2847 {
2848     return ( ( FLI_INPUT_SPEC * ) obj->spec )->screenlines;
2849 }
2850 
2851 
2852 #define SetKey( m )  if ( keymap->m ) kmap.m = keymap->m
2853 
2854 
2855 /***************************************
2856  ***************************************/
2857 
2858 void
fl_set_input_editkeymap(const FL_EditKeymap * keymap)2859 fl_set_input_editkeymap( const FL_EditKeymap * keymap )
2860 {
2861     /* If keymap is Null force default */
2862 
2863     if ( ! keymap )
2864     {
2865         set_default_keymap( 1 );
2866         return;
2867     }
2868 
2869     set_default_keymap( 0 );
2870 
2871     SetKey( del_prev_char );
2872     SetKey( del_next_char );
2873     SetKey( del_prev_word );
2874     SetKey( del_next_word );
2875 
2876     SetKey( moveto_prev_char );
2877     SetKey( moveto_next_char );
2878     SetKey( moveto_prev_word );
2879     SetKey( moveto_next_word );
2880     SetKey( moveto_prev_line );
2881     SetKey( moveto_next_line );
2882     SetKey( moveto_bof );
2883     SetKey( moveto_eof );
2884     SetKey( moveto_bol );
2885     SetKey( moveto_eol );
2886 
2887     SetKey( backspace );
2888     SetKey( clear_field );
2889     SetKey( paste );
2890     SetKey( transpose );
2891     SetKey( del_to_eos );
2892     SetKey( del_to_eol );
2893     SetKey( del_to_bol );
2894 }
2895 
2896 
2897 /***************************************
2898  ***************************************/
2899 
2900 void
fl_get_input_editkeymap(FL_EditKeymap * keymap)2901 fl_get_input_editkeymap( FL_EditKeymap * keymap )
2902 {
2903     /* Don't do anything if we got a NULL pointer */
2904 
2905     if ( ! keymap )
2906         return;
2907 
2908     /* Make sure the keymap is set up */
2909 
2910     set_default_keymap( 0 );
2911 
2912     /* Copy the current keymap to the user supplied buffer */
2913 
2914     memcpy( keymap, &kmap, sizeof kmap );
2915 }
2916 
2917 
2918 /***************************************
2919  ***************************************/
2920 
2921 static void
copy_attributes(FL_OBJECT * dest,const FL_OBJECT * src)2922 copy_attributes( FL_OBJECT       * dest,
2923                  const FL_OBJECT * src )
2924 {
2925     if ( src == dest )
2926         return;
2927 
2928     dest->col1    = src->col1;
2929     dest->col2    = src->col2;
2930     dest->align   = src->align;
2931     dest->boxtype = src->boxtype;
2932     dest->lcol    = src->lcol;
2933     dest->lstyle  = src->lstyle;
2934     dest->lsize   = src->lsize;
2935 }
2936 
2937 
2938 /***************************************
2939  ***************************************/
2940 
2941 static void
redraw_scrollbar(FL_OBJECT * obj)2942 redraw_scrollbar( FL_OBJECT * obj )
2943 {
2944     FLI_INPUT_SPEC *sp = obj->spec;
2945 
2946     fl_freeze_form( obj->form );
2947 
2948     if ( sp->v_on )
2949     {
2950         fl_set_scrollbar_size( sp->vscroll, sp->vsize );
2951         fl_set_scrollbar_value( sp->vscroll, sp->vval );
2952         if ( sp->vsize != 1.0 )
2953             fl_set_scrollbar_increment( sp->vscroll, sp->vinc1, sp->vinc2 );
2954         fl_redraw_object( sp->vscroll );
2955     }
2956 
2957     if ( sp->h_on )
2958     {
2959         fl_set_scrollbar_size( sp->hscroll, sp->hsize );
2960         fl_set_scrollbar_value( sp->hscroll, sp->hval );
2961         if ( sp->hsize != 1.0 )
2962             fl_set_scrollbar_increment( sp->hscroll, sp->hinc1, sp->hinc2 );
2963         fl_redraw_object( sp->hscroll );
2964     }
2965 
2966     if ( sp->attrib )
2967     {
2968         fl_redraw_object( sp->input );
2969         sp->attrib = 0;
2970     }
2971 
2972     if ( sp->dead_area && FL_ObjWin( obj ) )
2973     {
2974         sp->dead_area = 0;
2975         fl_winset( FL_ObjWin( obj ) );
2976         fl_draw_box( FL_FLAT_BOX, sp->dummy->x + sp->dummy->w - sp->vw,
2977                      sp->dummy->y + sp->dummy->h - sp->hh, sp->vw,
2978                      sp->hh, sp->hscroll->col1, 1 );
2979     }
2980 
2981     fl_unfreeze_form( obj->form );
2982 }
2983 
2984 
2985 /***************************************
2986  ***************************************/
2987 
2988 void
fl_set_input_cursor_visible(FL_OBJECT * obj,int visible)2989 fl_set_input_cursor_visible( FL_OBJECT * obj,
2990                              int         visible )
2991 {
2992     FLI_INPUT_SPEC *sp = obj->spec;
2993 
2994     if ( sp->cursor_visible != visible )
2995     {
2996         sp->cursor_visible = visible;
2997         fl_redraw_object( obj );
2998     }
2999 }
3000 
3001 
3002 /***************************************
3003  ***************************************/
3004 
3005 int
fl_input_changed(FL_OBJECT * obj)3006 fl_input_changed( FL_OBJECT * obj )
3007 {
3008     return ( ( FLI_INPUT_SPEC * ) obj->spec )->changed;
3009 }
3010 
3011 
3012 /***************************************
3013  ***************************************/
3014 
3015 int
fl_set_input_mode(int mode)3016 fl_set_input_mode( int mode )
3017 {
3018     int old_mode = Input_Mode;
3019 
3020     Input_Mode = mode == FL_DOS_INPUT_MODE ?
3021                  FL_DOS_INPUT_MODE : FL_NORMAL_INPUT_MODE;
3022 
3023     return old_mode;
3024 }
3025 
3026 
3027 /*
3028  * Local variables:
3029  * tab-width: 4
3030  * indent-tabs-mode: nil
3031  * End:
3032  */
3033