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 scrollbar.c
21  *
22  *  This file is part of the XForms library package.
23  *  Copyright (c) 1996-2002  T.C. Zhao
24  *  All rights reserved.
25  *
26  *  Scrollbar
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include "include/forms.h"
34 #include "flinternal.h"
35 #include "private/pscrollbar.h"
36 
37 
38 static void get_geom( FL_OBJECT * );
39 
40 static void attrib_change( FL_OBJECT * );
41 
42 
43 #define IsVThin( t )  (    t == FL_VERT_THIN_SCROLLBAR    \
44                         || t == FL_VERT_PLAIN_SCROLLBAR )
45 #define IsHThin( t )  (    t == FL_HOR_THIN_SCROLLBAR     \
46                         || t == FL_HOR_PLAIN_SCROLLBAR )
47 #define IsThin( t )   ( IsVThin( t ) || IsHThin( t ) )
48 
49 
50 /***************************************
51  ***************************************/
52 
53 static int
handle_scrollbar(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)54 handle_scrollbar( FL_OBJECT * obj,
55                   int         event,
56                   FL_Coord    mx   FL_UNUSED_ARG,
57                   FL_Coord    my   FL_UNUSED_ARG,
58                   int         key  FL_UNUSED_ARG,
59                   void      * ev   FL_UNUSED_ARG )
60 {
61     switch ( event )
62     {
63         case FL_ATTRIB :
64         case FL_RESIZED :
65             obj->align = fl_to_outside_lalign( obj->align );
66             attrib_change( obj );
67             get_geom( obj );
68             break;
69 
70         case FL_DRAW :
71             if ( IsThin( obj->type ) )
72                 fl_draw_box( obj->boxtype, obj->x, obj->y, obj->w, obj->h,
73                              obj->col1, obj->bw );
74             /* fall through */
75 
76         case FL_DRAWLABEL :
77             fl_draw_object_label_outside( obj );
78             break;
79 
80         case FL_FREEMEM :
81             /* children will take care of themselves */
82             fl_free( obj->spec );
83             break;
84     }
85 
86     return FL_RETURN_NONE;
87 }
88 
89 #define IS_HORIZ( o )  ( ( o )->type & FL_HOR_FLAG )
90 
91 
92 /***************************************
93  ***************************************/
94 
95 static void
attrib_change(FL_OBJECT * obj)96 attrib_change( FL_OBJECT * obj )
97 {
98     FLI_SCROLLBAR_SPEC *sp = obj->spec;
99 
100     sp->slider->col1 = obj->col1;
101     sp->slider->col2 = obj->col2;
102     sp->up->col1     = sp->down->col1    = obj->col1;
103     sp->up->col2     = sp->down->col2    = obj->col2;
104     sp->up->boxtype  = sp->down->boxtype = sp->slider->boxtype = obj->boxtype;
105 
106     fli_notify_object( sp->slider, FL_ATTRIB );
107 }
108 
109 
110 /***************************************
111  ***************************************/
112 
113 static void
get_geom(FL_OBJECT * obj)114 get_geom( FL_OBJECT * obj )
115 {
116     FLI_SCROLLBAR_SPEC *sp = obj->spec;
117     FL_OBJECT *up     = sp->up,
118               *down   = sp->down,
119               *slider = sp->slider;
120     int x = obj->x,
121         y = obj->y,
122         w = obj->w,
123         h = obj->h;
124     int absbw = FL_abs( obj->bw );
125     int t = obj->type;
126 
127     if ( IS_HORIZ( obj ) )
128     {
129         down->x = x;
130         up->x = x + w - h;
131         up->y = down->y = y;
132         down->h = up->h = h;
133         down->w = up->w = FL_min( w, h );
134 
135         slider->x = x + h;
136         slider->y = y;
137         slider->h = h;
138 
139         if ( ( slider->w = w - 2 * up->w ) < 0 )
140         {
141             slider->w = up->w / 3;
142             slider->x = x + up->w / 3;
143         }
144     }
145     else
146     {
147         up->x = down->x = x;
148         up->y = y;
149         up->w = down->w = w;
150         up->h = down->h = FL_min( w, h );
151 
152         slider->x = x;
153         slider->y = y + up->h;
154         slider->w = w;
155 
156         if ( ( slider->h = h - 2 * up->h ) < 0 )
157         {
158             slider->h = h / 3;
159             slider->y = y + up->h / 3;
160         }
161 
162         down->y = y + h - down->h;
163     }
164 
165     up->bw     = obj->bw;
166     down->bw   = obj->bw;
167     slider->bw = obj->bw;
168 
169     if ( absbw > 2 )
170         absbw--;
171 
172     if ( obj->bw > 0 )
173         up->bw = down->bw = absbw;
174     else
175         up->bw = down->bw = -absbw;
176 
177     if ( IsThin( t ) )
178     {
179         absbw = IS_FLATBOX( obj->boxtype ) ? 1 : absbw;
180 
181         up->boxtype = down->boxtype = FL_NO_BOX;
182         up->bw = down->bw = absbw;
183 
184         /* Due to slider double buffering we have to be completely clear of
185            the scrollbar bounding box, otherwise the slider will wipe out the
186            scrollbars bounding box */
187 
188         if ( IsVThin( t ) )
189         {
190             slider->x += absbw + 1;
191             slider->w -= 2 * absbw + 2;
192             slider->y -= absbw + ( absbw > 1 );
193             slider->h += 2 * absbw + ( absbw > 1 );
194         }
195         else
196         {
197             slider->y += absbw + 1;
198             slider->h -= 2 * absbw + 2;
199             slider->x -= absbw + ( absbw > 1 );
200             slider->w += 2 * absbw + ( absbw > 1 );
201         }
202     }
203 
204     fli_notify_object( slider, FL_RESIZED );
205 }
206 
207 
208 /***************************************
209  * Callback for the slider in the scrollbar
210  ***************************************/
211 
212 static void
slider_cb(FL_OBJECT * obj,long data FL_UNUSED_ARG)213 slider_cb( FL_OBJECT * obj,
214            long        data  FL_UNUSED_ARG )
215 {
216     FLI_SCROLLBAR_SPEC *sp = obj->parent->spec;
217 
218     if ( obj->returned & FL_RETURN_END )
219         obj->parent->returned |= FL_RETURN_END;
220 
221     if (    obj->parent->how_return & FL_RETURN_END_CHANGED
222          && obj->returned & FL_RETURN_END )
223     {
224         double nval = fl_get_slider_value( obj );
225 
226         if ( nval != sp->old_val )
227             obj->parent->returned |= FL_RETURN_CHANGED;
228         sp->old_val = nval;
229     }
230     else if ( obj->returned & FL_RETURN_CHANGED )
231         obj->parent->returned |= FL_RETURN_CHANGED;
232 }
233 
234 
235 /***************************************
236  * Callback for the buttons of the scrollbar
237  ***************************************/
238 
239 static void
button_cb(FL_OBJECT * obj,long data)240 button_cb( FL_OBJECT * obj,
241            long        data )
242 {
243     FLI_SCROLLBAR_SPEC *sp = obj->parent->spec;
244     double ival = fl_get_slider_value( sp->slider ),
245            nval = ival,
246            slmax,
247            slmin;
248 
249     /* Update the slider and get the new value */
250 
251     if ( obj->returned == FL_RETURN_TRIGGERED )
252         obj->returned = FL_RETURN_END | FL_RETURN_CHANGED;
253 
254     if ( obj->returned & FL_RETURN_CHANGED )
255     {
256         fl_get_slider_bounds( sp->slider, &slmin, &slmax );
257 
258         if ( slmax > slmin )
259             nval = ival + data * sp->increment;
260         else
261             nval = ival - data * sp->increment;
262 
263         fl_set_slider_value( sp->slider, nval );
264 
265         nval = fl_get_slider_value( sp->slider );
266     }
267 
268     if ( obj->returned & FL_RETURN_END )
269         obj->parent->returned |= FL_RETURN_END;
270 
271     /* If we're supposed to return only on end and change check if the
272        slider value changed since interaction started, if we have to return
273        on everty change check if if it changed this time round */
274 
275     if (    obj->parent->how_return & FL_RETURN_END_CHANGED
276          && obj->returned & FL_RETURN_END )
277     {
278         if ( nval != sp->old_val )
279         {
280             obj->parent->returned |= FL_RETURN_CHANGED;
281             sp->old_val = nval;
282         }
283     }
284     else if ( ival != nval )
285         obj->parent->returned |= FL_RETURN_CHANGED;
286 }
287 
288 
289 /***************************************
290  ***************************************/
291 
292 FL_OBJECT *
fl_create_scrollbar(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * l)293 fl_create_scrollbar( int          type,
294                      FL_Coord     x,
295                      FL_Coord     y,
296                      FL_Coord     w,
297                      FL_Coord     h,
298                      const char * l )
299 {
300     FLI_SCROLLBAR_SPEC *sp;
301     FL_OBJECT *obj;
302 
303     obj = fl_make_object( FL_SCROLLBAR, type, x, y, w, h, l, handle_scrollbar );
304 
305     obj->spec       = sp = fl_calloc( 1, sizeof *sp );
306     obj->col1       = FL_COL1;
307     obj->col2       = FL_COL1;
308     obj->align      = FL_SCROLLBAR_ALIGN;
309     obj->set_return = fl_set_scrollbar_return;
310 
311     if ( IsThin( type ) )
312         obj->boxtype = FL_DOWN_BOX;
313     else if ( type == FL_HOR_NICE_SCROLLBAR || type == FL_VERT_NICE_SCROLLBAR )
314         obj->boxtype = FL_FRAME_BOX;
315     else
316         obj->boxtype = FL_UP_BOX;
317 
318     if ( IS_HORIZ( obj ) )
319     {
320         fl_set_object_resize( obj, FL_RESIZE_X );
321 
322         sp->up   = fl_create_scrollbutton( FL_TOUCH_BUTTON, 1, 1, 1, 1, "6" );
323         sp->down = fl_create_scrollbutton( FL_TOUCH_BUTTON, 1, 1, 1, 1, "4" );
324         fl_set_object_callback( sp->up, button_cb, 1 );
325         fl_set_object_callback( sp->down, button_cb, -1 );
326         fl_set_object_resize( sp->up, FL_RESIZE_NONE );
327         fl_set_object_resize( sp->down, FL_RESIZE_NONE );
328 
329         if ( type == FL_HOR_SCROLLBAR )
330             sp->slider = fl_create_slider( FL_HOR_BROWSER_SLIDER2,
331                                            1, 1, 1, 1, "" );
332         else if ( type == FL_HOR_THIN_SCROLLBAR )
333             sp->slider = fl_create_slider( FL_HOR_THIN_SLIDER,
334                                            1, 1, 1, 1, "" );
335         else if ( type == FL_HOR_PLAIN_SCROLLBAR )
336             sp->slider = fl_create_slider( FL_HOR_BASIC_SLIDER,
337                                            1, 1, 1, 1, "" );
338         else if ( type == FL_HOR_NICE_SCROLLBAR )
339             sp->slider = fl_create_slider( FL_HOR_NICE_SLIDER2,
340                                            1, 1, 1, 1, "" );
341 
342         fl_set_object_resize( sp->slider, FL_RESIZE_NONE );
343     }
344     else
345     {
346         fl_set_object_resize( obj, FL_RESIZE_Y );
347 
348         sp->up = fl_create_scrollbutton( FL_TOUCH_BUTTON, 1, 1, 1, 1, "8" );
349         sp->down = fl_create_scrollbutton( FL_TOUCH_BUTTON, 1, 1, 1, 1, "2" );
350         fl_set_object_callback( sp->up, button_cb, -1 );
351         fl_set_object_callback( sp->down, button_cb, 1 );
352         fl_set_object_resize( sp->up, FL_RESIZE_NONE );
353         fl_set_object_resize( sp->down, FL_RESIZE_NONE );
354 
355         if ( type == FL_VERT_SCROLLBAR )
356             sp->slider = fl_create_slider( FL_VERT_BROWSER_SLIDER2, 1, 1,
357                                            1, 1, "" );
358         else if ( type == FL_VERT_THIN_SCROLLBAR )
359             sp->slider = fl_create_slider( FL_VERT_THIN_SLIDER, 1, 1,
360                                            1, 1, "" );
361         else if ( type == FL_VERT_PLAIN_SCROLLBAR )
362             sp->slider = fl_create_slider( FL_VERT_BASIC_SLIDER, 1, 1,
363                                            1, 1, "" );
364         else if ( type == FL_VERT_NICE_SCROLLBAR )
365             sp->slider = fl_create_slider( FL_VERT_NICE_SLIDER2, 1, 1,
366                                            1, 1, "" );
367         else
368             M_err( "fl_create_scrollbar", "Unknown type %d", type );
369 
370         fl_set_object_resize( sp->slider, FL_RESIZE_NONE );
371     }
372 
373     sp->increment = 0.02;
374     fl_set_slider_increment( sp->slider, 5 * sp->increment, sp->increment );
375     fl_set_object_callback( sp->slider, slider_cb, 0 );
376     fl_set_slider_bounds( sp->slider, 0.0, 1.0 );
377 
378     sp->old_val = fl_get_slider_value( sp->slider );
379 
380     fl_add_child( obj, sp->slider );
381     fl_add_child( obj, sp->down );
382     fl_add_child( obj, sp->up );
383 
384     /* In older versions scrollbars and browsers didn't return to the
385        application on e.g. fl_do_forms() but still a callback associated
386        with the object got called. To emulate the old behaviour we have
387        to set the return policy to default to FL_RETURN_NONE and only
388        change that to FL_RETURN_CHANGED when a callback is installed
389        (which is done in fl_set_object_callback()) */
390 
391 #if ! USE_BWC_BS_HACK
392     fl_set_object_return( obj, FL_RETURN_CHANGED );
393 #else
394     fl_set_object_return( obj, FL_RETURN_NONE );
395 #endif
396 
397     return obj;
398 }
399 
400 
401 /*
402  * User routines
403  */
404 
405 #define ISSCROLLBAR( o )  ( ( o ) && ( o )->objclass == FL_SCROLLBAR )
406 
407 
408 /***************************************
409  ***************************************/
410 
411 FL_OBJECT *
fl_add_scrollbar(int type,FL_Coord x,FL_Coord y,FL_Coord w,FL_Coord h,const char * l)412 fl_add_scrollbar( int          type,
413                   FL_Coord     x,
414                   FL_Coord     y,
415                   FL_Coord     w,
416                   FL_Coord     h,
417                   const char * l )
418 {
419     FL_OBJECT *obj = fl_create_scrollbar( type, x, y, w, h, l );
420 
421     attrib_change( obj );
422     get_geom( obj );
423     fl_add_object( fl_current_form, obj );
424 
425     return obj;
426 }
427 
428 
429 /***************************************
430  ***************************************/
431 
432 double
fl_get_scrollbar_value(FL_OBJECT * obj)433 fl_get_scrollbar_value( FL_OBJECT * obj )
434 {
435     FLI_SCROLLBAR_SPEC *sp = obj->spec;
436 
437     if ( ! ISSCROLLBAR( obj ) )
438     {
439         M_err( "fl_get_scrollbar_value", "%s not a scrollbar",
440                obj ? obj->label : "Object" );
441         return - HUGE_VAL;
442     }
443 
444     return fl_get_slider_value( sp->slider );
445 }
446 
447 
448 /***************************************
449  ***************************************/
450 
451 void
fl_set_scrollbar_value(FL_OBJECT * obj,double val)452 fl_set_scrollbar_value( FL_OBJECT * obj,
453                         double      val )
454 {
455     FLI_SCROLLBAR_SPEC *sp = obj->spec;
456 
457     if ( ! ISSCROLLBAR( obj ) )
458     {
459         M_err( "fl_set_scrollbar_value", "%s not a scrollbar",
460                obj ? obj->label : "Object" );
461         return;
462     }
463 
464     sp->old_val = val;
465     fl_set_slider_value( sp->slider, val );
466 }
467 
468 
469 /***************************************
470  * Sets the size of the knob of the scrollbar
471  * (the function name is a bit of misnomer)
472  ***************************************/
473 
474 void
fl_set_scrollbar_size(FL_OBJECT * obj,double val)475 fl_set_scrollbar_size( FL_OBJECT * obj,
476                        double      val )
477 {
478     FLI_SCROLLBAR_SPEC *sp = obj->spec;
479 
480     fl_set_slider_size( sp->slider, val );
481     get_geom( obj );
482 }
483 
484 
485 /***************************************
486  * Sets the size of the knob of the scrollbar
487  * (the function name is a bit of misnomer)
488  ***************************************/
489 
490 double
fl_get_scrollbar_size(FL_OBJECT * obj)491 fl_get_scrollbar_size( FL_OBJECT * obj )
492 {
493     return fl_get_slider_size( ( ( FLI_SCROLLBAR_SPEC * ) obj->spec )->slider );
494 }
495 
496 
497 /***************************************
498  ***************************************/
499 
500 void
fl_set_scrollbar_increment(FL_OBJECT * obj,double l,double r)501 fl_set_scrollbar_increment( FL_OBJECT * obj,
502                             double      l,
503                             double      r )
504 {
505     FLI_SCROLLBAR_SPEC *sp = obj->spec;
506 
507     fl_set_slider_increment( sp->slider, l, r );
508     sp->increment = r;
509 }
510 
511 
512 /***************************************
513  ***************************************/
514 
515 void
fl_get_scrollbar_increment(FL_OBJECT * obj,double * a,double * b)516 fl_get_scrollbar_increment( FL_OBJECT * obj,
517                             double    * a,
518                             double    * b )
519 {
520     FLI_SCROLLBAR_SPEC *sp = obj->spec;
521 
522     fl_get_slider_increment( sp->slider, a, b );
523 }
524 
525 
526 /***************************************
527  ***************************************/
528 
529 void
fl_set_scrollbar_bounds(FL_OBJECT * obj,double b1,double b2)530 fl_set_scrollbar_bounds( FL_OBJECT * obj,
531                          double      b1,
532                          double      b2 )
533 {
534     FLI_SCROLLBAR_SPEC *sp = obj->spec;
535 
536     if ( ! ISSCROLLBAR( obj ) )
537     {
538         M_err( "fl_set_scrollbar_bounds", "%s not a scrollbar",
539                obj ? obj->label : "Object" );
540         return;
541     }
542 
543     fl_set_slider_bounds( sp->slider, b1, b2 );
544 }
545 
546 
547 /***************************************
548  ***************************************/
549 
550 void
fl_get_scrollbar_bounds(FL_OBJECT * obj,double * b1,double * b2)551 fl_get_scrollbar_bounds( FL_OBJECT * obj,
552                          double    * b1,
553                          double    * b2 )
554 {
555     FLI_SCROLLBAR_SPEC *sp = obj->spec;
556 
557     fl_get_slider_bounds( sp->slider, b1, b2 );
558 }
559 
560 
561 /***************************************
562  * Sets under which conditions the object is to be returned to the
563  * application. This function should be regarded as for internal use
564  * only and fl_set_object_return() should be used instead (which then
565  * will call this function).
566  ***************************************/
567 
568 void
fl_set_scrollbar_return(FL_OBJECT * obj,unsigned int when)569 fl_set_scrollbar_return( FL_OBJECT    * obj,
570                          unsigned int   when )
571 {
572     FLI_SCROLLBAR_SPEC *sp = obj->spec;
573 
574     if ( when & FL_RETURN_END_CHANGED )
575         when &= ~ ( FL_RETURN_NONE | FL_RETURN_CHANGED );
576 
577     obj->how_return = when;
578 
579     fl_set_object_return( sp->slider, FL_RETURN_ALWAYS );
580     fl_set_object_return( sp->up,     FL_RETURN_ALWAYS );
581     fl_set_object_return( sp->down,   FL_RETURN_ALWAYS );
582 
583     /* We may need the value of the slider at this moment in the
584        callback function... */
585 
586     sp->old_val = fl_get_slider_value( sp->slider );
587 }
588 
589 
590 /***************************************
591  ***************************************/
592 
593 void
fl_set_scrollbar_step(FL_OBJECT * obj,double step)594 fl_set_scrollbar_step( FL_OBJECT * obj,
595                        double      step )
596 {
597     FLI_SCROLLBAR_SPEC *sp = obj->spec;
598 
599     fl_set_slider_step( sp->slider, step );
600 }
601 
602 
603 /***************************************
604  ***************************************/
605 
606 int
fl_get_scrollbar_repeat(FL_OBJECT * obj)607 fl_get_scrollbar_repeat( FL_OBJECT * obj )
608 {
609     return
610         fl_get_slider_repeat( ( ( FLI_SCROLLBAR_SPEC * ) obj->spec )->slider );
611 }
612 
613 
614 /***************************************
615  ***************************************/
616 
617 void
fl_set_scrollbar_repeat(FL_OBJECT * obj,int millisec)618 fl_set_scrollbar_repeat( FL_OBJECT * obj,
619                          int         millisec )
620 {
621     fl_set_slider_repeat( ( ( FLI_SCROLLBAR_SPEC * ) obj->spec )->slider,
622                           millisec );
623 }
624 
625 
626 /*
627  * Local variables:
628  * tab-width: 4
629  * indent-tabs-mode: nil
630  * End:
631  */
632