1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        jan@swi.psy.uva.nl
5     WWW:           http://www.swi.psy.uva.nl/projects/xpce/
6     Copyright (c)  1985-2002, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 
38 #ifdef __WINDOWS__			/* timer broken in Win95 */
39 #define SCROLLTIMER_USE_SLEEP 1
40 #endif
41 
42 #define OPENLOOK 1
43 #define O_MOTIF 1
44 
45 #if OPENLOOK
46 #define BOXHEIGHT	6		/* boxes at top/bottom */
47 #define BAR_WIDTH	3		/* Width of bar-line */
48 #define BOX_MARGIN	2		/* Box to bar-line */
49 #define BUTTON_HEIGHT  15		/* Height of (1/3) button */
50 static  int LastOffset;			/* pointer-warping */
51 #endif
52 
53 #define swap(x, y)	{ int z; z=x; x=y; y=z; }
54 #define swapInt(x, y)	{ Int z; z=x; x=y; y=z; }
55 #define BOUNDS(n, l, h) ((n) > (h) ? (h) : (n) < (l) ? (l) : (n))
56 #define MIN_BUBBLE 6			/* smallest variable bubble */
57 
58 #define Repeating(sb) ((sb)->status == NAME_repeatDelay || \
59 		       (sb)->status == NAME_repeat)
60 
61 typedef struct bubble_info *BubbleInfo;
62 typedef struct sb_draw_data *SbDrawData;
63 
64 struct bubble_info
65 { int	start;				/* start of bubble */
66   int	length;				/* lenght of bubble */
67   int	bar_start;			/* start of the bar */
68   int   bar_length;			/* length of the bar */
69 };
70 
71 struct sb_draw_data
72 { int x, y, w, h;			/* from initialiseDeviceGraphical() */
73   int vertical;				/* true if vertical bar */
74   int arrow;				/* height of arrows */
75   struct bubble_info bubble;		/* bubble info */
76 };
77 
78 
79 static void	compute_bubble(ScrollBar, struct bubble_info *,
80 			       int start, int min_bubble,
81 			       int fixed_bubble);
82 static status	orientationScrollBar(ScrollBar s, Name or);
83 static status	lookScrollBar(ScrollBar s, Name look);
84 static status   OpenLookRedrawAreaScrollBar(ScrollBar s, Area a);
85 static int	offset_event_scrollbar(ScrollBar s, EventObj ev);
86 static status	OpenLookEventScrollBar(ScrollBar s, EventObj ev);
87 static status	MotifEventScrollBar(ScrollBar s, EventObj ev);
88 static status	forwardScrollBar(ScrollBar s);
89 static Int	promilage_event_scrollbar(ScrollBar s, EventObj ev);
90 static status	detachTimerScrollBar(ScrollBar s);
91 
92 static status
initialiseScrollBar(ScrollBar s,Any obj,Name orientation,Message msg)93 initialiseScrollBar(ScrollBar s, Any obj, Name orientation, Message msg)
94 { Int w = getClassVariableValueObject(s, NAME_width);
95 
96   if ( !w || isDefault(w) )
97     w = ws_default_scrollbar_width();
98 
99   initialiseGraphical(s, ZERO, ZERO, w, toInt(100));
100   assign(s, cursor, getClassVariableValueObject(s, NAME_cursor));
101   assign(s, orientation,   NAME_vertical);
102 
103   assign(s, view,	   toInt(-1));	/* length of view */
104   assign(s, start,	   toInt(-1));	/* position in object */
105   assign(s, length,	   toInt(-1));	/* length of scrollable object */
106 
107   assign(s, bubble_start,  toInt(-1));
108   assign(s, bubble_length, toInt(-1));
109 
110   assign(s, message, 	   msg);
111   assign(s, object,	   obj);
112 
113   assign(s, drag,	   ON);
114   assign(s, amount,	   ZERO);
115   assign(s, direction,	   NAME_forwards);
116   assign(s, unit,	   NAME_file);
117   assign(s, status,	   NAME_inactive);
118   assign(s, offset,	   ZERO);
119 
120   obtainClassVariablesObject(s);
121   if ( orientation == NAME_horizontal )
122     orientationScrollBar(s, orientation);
123 
124   requestComputeGraphical(s, DEFAULT);
125 
126   succeed;
127 }
128 
129 
130 		/********************************
131 		*            COMPUTE		*
132 		********************************/
133 
134 static int
arrow_height_scrollbar(ScrollBar sb)135 arrow_height_scrollbar(ScrollBar sb)
136 { if ( sb->look == NAME_motif ||
137        sb->look == NAME_gtk ||
138        sb->look == NAME_win )
139   { int ah;
140 
141     if ( (ah = ws_arrow_height_scrollbar(sb)) < 0 )
142     { ah = valInt(sb->orientation == NAME_vertical ? sb->area->w
143 		  				   : sb->area->h);
144     }
145 
146     return ah;
147   }
148 
149   return 0;
150 }
151 
152 
153 static status
ComputeScrollBar(ScrollBar sb)154 ComputeScrollBar(ScrollBar sb)
155 { if ( notNil(sb->request_compute) )
156   { struct bubble_info bi;
157     int arrow = arrow_height_scrollbar(sb);
158 
159     compute_bubble(sb, &bi, arrow, MIN_BUBBLE, FALSE);
160     if ( valInt(sb->bubble_start) != bi.start ||
161 	 valInt(sb->bubble_length) != bi.length )
162     { DEBUG(NAME_scroll, Cprintf("%s: start %ld --> %d; length %ld --> %d\n",
163 				 pp(sb), valInt(sb->bubble_start), bi.start,
164 				 valInt(sb->bubble_length), bi.length));
165       assign(sb, bubble_start, toInt(bi.start));
166       assign(sb, bubble_length, toInt(bi.length));
167 
168 #if OPENLOOK
169       if ( sb->look == NAME_openLook && Repeating(sb) )
170       { struct bubble_info button_bi;
171 
172 	compute_bubble(sb, &button_bi,
173 		       BOXHEIGHT+BOX_MARGIN, BUTTON_HEIGHT*3, TRUE);
174 
175 	assign(sb, request_compute, NIL);	/* avoid loop */
176 
177 	if ( sb->unit == NAME_line )
178 	{ int y;
179 
180 	  if ( sb->direction == NAME_backwards )
181 	    y = button_bi.start + BUTTON_HEIGHT/2;
182 	  else
183 	    y = button_bi.start + BUTTON_HEIGHT*2 + BUTTON_HEIGHT/2;
184 
185 	  if ( sb->orientation == NAME_vertical )
186 	    pointerGraphical((Graphical) sb,
187 			     answerObject(ClassPoint,
188 					  div(sb->area->w, TWO),
189 					  toInt(y), EAV));
190 	  else
191 	    pointerGraphical((Graphical) sb,
192 			     answerObject(ClassPoint,
193 					  toInt(y),
194 					  div(sb->area->h, TWO), EAV));
195 	} else if ( sb->unit == NAME_page )
196 	{ int y = -1;
197 
198 	  if ( sb->direction == NAME_backwards &&
199 	       LastOffset >= button_bi.start )
200 	    y = button_bi.start - 1;
201 	  else if ( sb->direction == NAME_forwards &&
202 		    LastOffset <= button_bi.start + button_bi.length )
203 	    y = button_bi.start + button_bi.length + 1;
204 
205 	  if ( y > 0 )
206 	  { if ( sb->orientation == NAME_vertical )
207 	      pointerGraphical((Graphical) sb,
208 			       answerObject(ClassPoint,
209 					  div(sb->area->w, TWO),
210 					  toInt(y), EAV));
211 	    else
212 	      pointerGraphical((Graphical) sb,
213 			     answerObject(ClassPoint,
214 					  toInt(y),
215 					  div(sb->area->h, TWO), EAV));
216 
217 	    LastOffset = y;
218 	  }
219 	}
220 
221 /*	assign(sb, status, NAME_inactive); */
222       }
223 #endif /*OPENLOOK*/
224       CHANGING_GRAPHICAL(sb, changedEntireImageGraphical(sb));
225     }
226 
227     assign(sb, request_compute, NIL);
228   }
229 
230   succeed;
231 }
232 
233 
234 Int
getMarginScrollBar(ScrollBar sb)235 getMarginScrollBar(ScrollBar sb)
236 { if ( sb->displayed == OFF )
237     answer(ZERO);
238 
239   if ( sb->orientation == NAME_horizontal )
240   { if ( memberChain(sb->placement, NAME_bottom) )
241       answer(add(sb->area->h, sb->distance));
242     else
243       answer(minInt(add(sb->area->h, sb->distance)));
244   } else				/* vertical */
245   { if ( memberChain(sb->placement, NAME_right) )
246       answer(add(sb->area->w, sb->distance));
247     else
248       answer(minInt(add(sb->area->w, sb->distance)));
249   }
250 }
251 
252 
253 status
placeScrollBar(ScrollBar sb,Graphical gr)254 placeScrollBar(ScrollBar sb, Graphical gr)
255 { if ( isDefault(gr) )
256     gr = sb->object;
257 
258   if ( instanceOfObject(gr, ClassGraphical) )
259   { if ( sb->orientation == NAME_horizontal )
260     { setGraphical(sb,
261 		   gr->area->x,
262 		   memberChain(sb->placement, NAME_bottom)
263 		       ? add(gr->area->y, add(gr->area->h, sb->distance))
264 		       : sub(gr->area->y, add(sb->area->h, sb->distance)),
265 		   gr->area->w,
266 		   DEFAULT);
267     } else				/* vertical */
268     { setGraphical(sb,
269 		   memberChain(sb->placement, NAME_right)
270 		        ? add(gr->area->x, add(gr->area->w, sb->distance))
271 		        : sub(gr->area->x, add(sb->area->w, sb->distance)),
272 		   gr->area->y,
273 		   DEFAULT,
274 		   gr->area->h);
275     }
276   }
277 
278   succeed;
279 }
280 
281 
282 static status
computeScrollBar(ScrollBar sb)283 computeScrollBar(ScrollBar sb)
284 { if ( notNil(sb->request_compute) )
285   { if ( hasSendMethodObject(sb->object, NAME_bubbleScrollBar) )
286       send(sb->object, NAME_bubbleScrollBar, sb, EAV);
287     else if ( hasGetMethodObject(sb->object, NAME_start) &&
288 	      hasGetMethodObject(sb->object, NAME_view) &&
289 	      hasGetMethodObject(sb->object, NAME_length) )
290     { Int s = getv(sb->object, NAME_start, 0, NULL);
291       Int v = getv(sb->object, NAME_view, 0, NULL);
292       Int l = getv(sb->object, NAME_length, 0, NULL);
293 
294       if ( s != FAIL && v != FAIL && l != FAIL )
295 	bubbleScrollBar(sb, l, s, v);
296 
297     }
298 
299     ComputeScrollBar(sb);
300   }
301 
302   succeed;
303 }
304 
305 
306 static void
compute_bubble(ScrollBar s,struct bubble_info * bi,int bar_start,int min_bubble,int fixed_bubble)307 compute_bubble(ScrollBar s, struct bubble_info *bi,
308 	       int bar_start, int min_bubble, int fixed_bubble)
309 { int len   = valInt(s->length);
310   int start = (valInt(s->start) > len ? len : valInt(s->start));
311   int view  = valInt(s->view);
312 
313 					/* bar sizes */
314   bi->bar_start   = bar_start;
315   bi->bar_length  = (s->orientation == NAME_vertical
316 		         ? valInt(s->area->h)
317 		         : valInt(s->area->w));
318   bi->bar_length -= 2 * bi->bar_start;
319 
320 					/* bubble characteristics */
321   if ( fixed_bubble )
322   { int free, below, above;
323 
324     if ( bi->bar_length < min_bubble )
325     { bi->bar_length += 2 * bi->bar_start;
326       bi->bar_start = 0;
327 
328       if ( bi->bar_length < min_bubble )
329 	min_bubble = bi->bar_length;
330     }
331 
332     bi->length = min_bubble;
333     free  = bi->bar_length - bi->length;
334     below = len - (start+view);
335     above = start;
336 
337     if ( (above + below) <= 0 )
338       bi->start = 0;
339     else
340       bi->start = (free * above) / (above + below);
341   } else
342   { double bubble_prom = (len != 0 ? (double)start/(double)len
343 			           : 0.0);
344     double bubble_lp   = (len != 0 ? (double)valInt(s->view)/(double)len
345 				   : 1.0);
346 
347     bi->length = (int)((double)bi->bar_length * bubble_lp) + min_bubble;
348     bi->start  = (int)((double)bi->bar_length * bubble_prom) - min_bubble/2;
349   }
350 
351   bi->start    = BOUNDS(bi->start, 0, bi->bar_length-min_bubble);
352   bi->start   += bi->bar_start;
353   bi->length   = BOUNDS(bi->length, 0,
354 			(bi->bar_length + bi->bar_start - bi->start));
355 }
356 
357 
358 		 /*******************************
359 		 *		REDRAW		*
360 		 *******************************/
361 
362 static void
sb_init_draw_data(ScrollBar s,Area a,SbDrawData d,Any bg)363 sb_init_draw_data(ScrollBar s, Area a, SbDrawData d, Any bg)
364 { int m;
365 
366   initialiseDeviceGraphical(s, &d->x, &d->y, &d->w, &d->h);
367   NormaliseArea(d->x, d->y, d->w, d->h);
368 
369   if ( instanceOfObject(bg, ClassElevation) )
370   { Elevation z = bg;
371 
372     r_3d_box(d->x, d->y, d->w, d->h, 0, bg, TRUE);
373 
374     m = labs(valInt(z->height)) + 1;
375     d->x += m;
376     d->y += m;
377     d->w -= 2*m;
378     d->h -= 2*m;
379   } else
380     m = 0;
381 
382   d->vertical = (s->orientation == NAME_vertical);
383   d->arrow = arrow_height_scrollbar(s);
384   compute_bubble(s, &d->bubble,
385 		 m ? d->arrow - 1 : d->arrow,
386 		 MIN_BUBBLE, FALSE);
387   d->bubble.start -= m;
388   d->arrow -= 2*m;
389 }
390 
391 static Elevation
getElevationScrollBar(ScrollBar s)392 getElevationScrollBar(ScrollBar s)
393 { Elevation z = getClassVariableValueObject(s, NAME_elevation);
394 
395 					/* TBD: make default one */
396   return z;
397 }
398 
399 
400 static void
draw_arrow(ScrollBar s,int x,int y,int w,int h,Name which,int up)401 draw_arrow(ScrollBar s, int x, int y, int w, int h, Name which, int up)
402 { if ( !ws_draw_scrollbar_arrow(s, x, y, w, h, which, up) )
403   { Elevation z = getElevationScrollBar(s);
404 
405     DEBUG(NAME_arrow, Cprintf("Arrow box(%d, %d, %d, %d)\n", x, y, w, h));
406 
407     if ( s->look == NAME_win ||
408          s->look == NAME_gtk )
409     { Image img;
410       int iw, ih;
411 
412       r_thickness(valInt(s->pen));
413 
414       if ( up )
415 	r_3d_box(x, y, w, h, 0, z, TRUE);
416       else
417 	r_box(x, y, w, h, 0, isDefault(z->colour) ? NIL : (Any) z->colour);
418 
419            if ( which == NAME_up )       img = SCROLL_UP_IMAGE;
420       else if ( which == NAME_down )     img = SCROLL_DOWN_IMAGE;
421       else if ( which == NAME_left )     img = SCROLL_LEFT_IMAGE;
422       else /* ( which == NAME_right ) */ img = SCROLL_RIGHT_IMAGE;
423 
424       iw = valInt(img->size->w);
425       ih = valInt(img->size->h);
426 
427       r_image(img, 0, 0, x+(w-iw)/2, y+(h-ih)/2, iw, ih, ON);
428     } else				/* motif */
429     {
430     }
431   }
432 }
433 
434 
435 static void
draw_arrows(ScrollBar s,SbDrawData d)436 draw_arrows(ScrollBar s, SbDrawData d)
437 { int faup = TRUE;			/* first-arrow-up */
438   int saup = TRUE;			/* second-arrow-up */
439   int ah = d->arrow;
440 
441   if ( Repeating(s) && s->unit == NAME_line )
442   { if ( s->direction == NAME_forwards )
443       saup = FALSE;
444     else
445       faup = FALSE;
446   }
447 
448   if ( d->vertical )
449   { draw_arrow(s, d->x, d->y, d->w, ah, NAME_up, faup);
450     draw_arrow(s, d->x, d->y + d->h - ah, d->w, ah, NAME_down, saup);
451   } else
452   { draw_arrow(s, d->x, d->y, ah, d->h, NAME_left, faup);
453     draw_arrow(s, d->x + d->w-ah, d->y, ah, d->h, NAME_right, saup);
454   }
455 }
456 
457 
458 static void
draw_bubble(ScrollBar s,SbDrawData d)459 draw_bubble(ScrollBar s, SbDrawData d)
460 { int p = valInt(s->pen);
461   Elevation z = getClassVariableValueObject(s, NAME_elevation);
462   int x = d->x, y = d->y, w = d->w, h = d->h;
463   BubbleInfo bi = &d->bubble;
464   int pf=FALSE, pb=FALSE;
465 
466   if ( !instanceOfObject(z, ClassElevation) )
467     z = NULL;
468 
469   if ( s->look == NAME_win &&
470        Repeating(s) &&
471        s->unit == NAME_page )
472   { if ( s->direction == NAME_forwards )
473       pf = TRUE;
474     else
475       pb = TRUE;
476   }
477 
478   if ( d->vertical )
479   { int ym, hm;
480 
481     x += p;
482     w -= 2*p;
483 
484     ym = y+bi->bar_start; hm = bi->start - bi->bar_start;
485     if ( pb )
486       r_fill(x, ym, w, hm, BLACK_COLOUR);
487     else if ( s->look == NAME_win )
488       r_fill(x, ym, w, hm, GREY50_IMAGE);
489     else
490       r_clear(x, ym, w, hm);
491 
492     ym = y+bi->start;
493     hm = bi->length;
494     if ( !ws_draw_sb_thumb(x, ym, w, hm) )
495     { if ( z )
496 	r_3d_box(x, ym, w, hm, 0, z, TRUE);
497       else
498 	r_fill(x, ym, w, hm, GREY50_IMAGE);
499     }
500 
501     ym += hm;
502     hm = (bi->bar_start+bi->bar_length) - (bi->start+bi->length);
503     if ( hm > 0 )
504     { if ( pf )
505 	r_fill(x, ym, w, hm, BLACK_COLOUR);
506       else if ( s->look == NAME_win && z )
507 	r_fill(x, ym, w, hm, GREY50_IMAGE);
508       else
509 	r_clear(x, ym, w, hm);
510     }
511   } else /* horizontal */
512   { int xm, wm;
513 
514     y += p;
515     h -= 2*p;
516 
517     xm = x+bi->bar_start; wm = bi->start - bi->bar_start;
518     if ( pb )
519       r_fill(xm, y, wm, h, BLACK_COLOUR);
520     else if ( s->look == NAME_win && z )
521       r_fill(xm, y, wm, h, GREY50_IMAGE);
522     else
523       r_clear(xm, y, wm, h);
524 
525     xm = x+bi->start;
526     wm = bi->length;
527     if ( !ws_draw_sb_thumb(xm, y, wm, h) )
528     { if ( z )
529 	r_3d_box(xm, y, wm, h, 0, z, TRUE);
530       else
531 	r_fill(xm, y, wm, h, GREY50_IMAGE);
532     }
533 
534     xm += wm;
535     wm = (bi->bar_start+bi->bar_length) - (bi->start+bi->length);
536     if ( wm > 0 )
537     { if ( pf )
538 	r_fill(xm, y, wm, h, BLACK_COLOUR);
539       else if ( s->look == NAME_win && z )
540 	r_fill(xm, y, wm, h, GREY50_IMAGE);
541       else
542 	r_clear(xm, y, wm, h);
543     }
544   }
545 }
546 
547 
548 static status
RedrawAreaScrollBar(ScrollBar s,Area a)549 RedrawAreaScrollBar(ScrollBar s, Area a)
550 { Any bg = getClassVariableValueObject(s, NAME_background);
551   Any obg = NIL;
552   Elevation z = NIL;
553 
554   if ( bg )
555   { if ( instanceOfObject(bg, ClassColour) ||
556 	 instanceOfObject(bg, ClassPixmap) )
557       obg = r_background(bg);
558     else if ( instanceOfObject(bg, ClassElevation) )
559     { z = bg;
560       if ( instanceOfObject(z->colour, ClassColour) )
561 	obg = r_background(z->colour);
562     }
563   }
564 
565   if ( s->look == NAME_openLook )
566   { OpenLookRedrawAreaScrollBar(s, a);
567   } else
568   { struct sb_draw_data d;
569 
570     sb_init_draw_data(s, a, &d, z);
571     draw_bubble(s, &d);
572     if ( d.arrow )
573       draw_arrows(s, &d);
574   }
575 
576   if ( notNil(obg) )
577     r_background(obg);
578 
579   return RedrawAreaGraphical(s, a);
580 }
581 
582 
583 #if OPENLOOK
584 
585 static Timer	ScrollBarRepeatTimer;
586 static Message  ScrollBarRepeatMessage;
587 
588 static Timer
scrollBarRepeatTimer()589 scrollBarRepeatTimer()
590 { if ( !ScrollBarRepeatTimer )
591     ScrollBarRepeatTimer = globalObject(NAME_scrollBarRepeatTimer,
592 					ClassTimer, CtoReal(0.08),
593 					( ScrollBarRepeatMessage =
594 					  newObject(ClassMessage, NIL,
595 						    NAME_repeat, EAV)), EAV);
596   return ScrollBarRepeatTimer;
597 }
598 
599 
600 static status
changedBubbleScrollBar(ScrollBar s)601 changedBubbleScrollBar(ScrollBar s)
602 { if ( s->look == NAME_openLook )
603   { struct bubble_info button_bi;
604     compute_bubble(s, &button_bi,
605 		   BOXHEIGHT+BOX_MARGIN, BUTTON_HEIGHT*3, TRUE);
606 
607     if ( s->orientation == NAME_vertical )
608       changedImageGraphical(s, ZERO, toInt(button_bi.start),
609 			    s->area->w, toInt(button_bi.length));
610     else
611       changedImageGraphical(s, toInt(button_bi.start), ZERO,
612 			    toInt(button_bi.length), s->area->h);
613   }
614 
615   succeed;
616 }
617 
618 
619 static status
repeatScrollBar(ScrollBar s)620 repeatScrollBar(ScrollBar s)
621 { again:
622 
623   if ( getIsDisplayedGraphical((Graphical)s, DEFAULT) != ON )
624   { DEBUG(NAME_repeat, Cprintf("%s: no longer displayed\n", pp(s)));
625 
626     detachTimerScrollBar(s);
627     fail;
628   }
629 
630   if ( Repeating(s) )
631   { unsigned long clk = mclock();
632 
633     if ( s->unit == NAME_page )
634     { if ( s->direction == NAME_backwards )
635       { if ( valInt(s->start) <= 0 )
636 	{ detachTimerScrollBar(s);
637 	  succeed;
638 	}
639       } else if ( valInt(s->view) + valInt(s->start) >= valInt(s->length) )
640       { detachTimerScrollBar(s);
641 	succeed;
642       }
643     }
644     forwardScrollBar(s);
645     synchroniseGraphical((Graphical) s, ON);
646     if ( Repeating(s) )		/* synchroniseGraphical() can handle up */
647     { Real t = getClassVariableValueObject(s, NAME_repeatInterval);
648       int ct = (int)(valReal(t) * 1000.0) - (float)(mclock() - clk);
649 
650       assign(s, status, NAME_repeat);
651 
652       if ( ct > 5 )
653       {
654 #ifdef SCROLLTIMER_USE_SLEEP
655 	msleep(ct);
656 	goto again;
657 #else
658         Timer tmr = scrollBarRepeatTimer();
659 
660 	intervalTimer(tmr, CtoReal((float)ct / 1000.0));
661 	statusTimer(tmr, NAME_once);
662 #endif
663       } else
664 	goto again;
665     }
666   }
667 
668   succeed;
669 }
670 
671 
672 static status
detachTimerScrollBar(ScrollBar s)673 detachTimerScrollBar(ScrollBar s)
674 { if ( ScrollBarRepeatMessage && ScrollBarRepeatMessage->receiver == s )
675   { stopTimer(ScrollBarRepeatTimer);
676     assign(ScrollBarRepeatMessage, receiver, NIL);
677 
678     succeed;
679   }
680 
681   fail;
682 }
683 
684 
685 static void
attachTimerScrollBar(ScrollBar s)686 attachTimerScrollBar(ScrollBar s)
687 { Timer t = scrollBarRepeatTimer();
688 
689   detachTimerScrollBar(s);
690   intervalTimer(t, getClassVariableValueObject(s, NAME_repeatDelay));
691   assign(ScrollBarRepeatMessage, receiver, s);
692   startTimer(t, NAME_once);
693 }
694 
695 
696 static status
unlinkScrollBar(ScrollBar s)697 unlinkScrollBar(ScrollBar s)
698 { detachTimerScrollBar(s);
699 
700   return unlinkGraphical((Graphical) s);
701 }
702 
703 
704 static void
sb_box(int x,int y,int w,int h,int vertical,Elevation z,int use_shadow,int active)705 sb_box(int x, int y, int w, int h,
706        int vertical, Elevation z, int use_shadow,
707        int active)
708 { if ( !vertical )
709   { swap(x, y);
710     swap(w, h);
711   }
712 
713   if ( !z )
714   { if ( use_shadow )
715       r_shadow_box(x, y, w, h, 0, 1, active ? BLACK_IMAGE : NIL);
716     else
717       r_box(x, y, w, h, 0, active ? BLACK_IMAGE : NIL);
718   } else
719   { r_3d_box(x, y, w, h, 0, z, active ? FALSE : TRUE);
720   }
721 }
722 
723 
724 static void
sb_triangle(int x1,int y1,int x2,int y2,int x3,int y3,int vertical,Elevation z,int active)725 sb_triangle(int x1, int y1,
726 	    int x2, int y2,
727 	    int x3, int y3,
728 	    int vertical, Elevation z,
729 	    int active)
730 { Any fill = ((z || active) ? BLACK_IMAGE : GREY50_IMAGE);
731 
732   if ( !vertical )
733   { swap(x1, y1);
734     swap(x2, y2);
735     swap(x3, y3);
736   }
737 
738   r_triangle(x1, y1, x2, y2, x3, y3, fill);
739 }
740 
741 
742 static status
OpenLookRedrawAreaScrollBar(ScrollBar s,Area a)743 OpenLookRedrawAreaScrollBar(ScrollBar s, Area a)
744 { int x, y, w, h;
745   int p = valInt(s->pen);
746   int shadow = 1;
747   int am = 3;				/* arrow-margin */
748   struct bubble_info bar_bi;
749   struct bubble_info button_bi;
750   iarea redraw;
751   Elevation z = getClassVariableValueObject(s, NAME_elevation);
752   int boxh = BOXHEIGHT;
753   int boxm = BOX_MARGIN;
754 
755   if ( isNil(z) )
756     z = NULL;
757 
758   initialiseRedrawAreaGraphical(s, a, &x, &y, &w, &h, &redraw);
759   r_clear(redraw.x, redraw.y, redraw.w, redraw.h);
760   r_thickness(p);
761   r_dash(s->texture);
762 
763   compute_bubble(s, &button_bi,
764 		 boxh+boxm, BUTTON_HEIGHT*3, TRUE);
765   compute_bubble(s, &bar_bi,
766 		 boxh+boxm, button_bi.length, FALSE);
767   if ( button_bi.bar_start == 0 )
768     boxh = 0;
769 
770 #define Fill(x, y, w, h, f) \
771 	if ( vertical ) r_fill(x, y, w, h, f); else r_fill(y, x, h, w, f)
772 #define Clear(x, y, w, h) \
773 	if ( vertical ) r_clear(x, y, w, h); else r_clear(y, x, h, w)
774 
775   { int vertical = (s->orientation == NAME_vertical);
776     int bx, cy, bh, ch, ch3, ch9, l1y, l2y;
777 
778     if ( !vertical )
779     { swap(x, y);
780       swap(w, h);
781     }
782 
783     x += 1, w -= 2;			/* 1 pixel border */
784     bx = x + (w-BAR_WIDTH+1)/2;		/* x of the bar */
785 
786     if ( boxh > 0 )
787     { sb_box(x, y, w, boxh, vertical, z, FALSE,
788 	     s->status == NAME_topOfFile);
789       sb_box(x, y+h-boxh, w, boxh, vertical, z, FALSE,
790 	     s->status == NAME_bottomOfFile);
791     }
792 
793     cy = y + bar_bi.bar_start;		/* paint the bar */
794     ch = y+bar_bi.start - cy;
795     Fill(bx, cy, BAR_WIDTH, ch, GREY50_IMAGE);
796     cy += ch;
797     ch = bar_bi.length;
798     Fill(bx, cy, BAR_WIDTH, ch, BLACK_IMAGE);
799     cy += ch;
800     ch = y + bar_bi.bar_start + bar_bi.bar_length - cy;
801     Fill(bx, cy, BAR_WIDTH, ch, GREY50_IMAGE);
802 
803     cy = y+button_bi.start;		/* paint the button */
804     bh = button_bi.length;
805     Clear(x, cy-1, w, bh+2);
806     sb_box(x, cy, w, bh, vertical, z, TRUE, FALSE);
807     ch3 = bh/3;
808     l1y = cy + ch3;
809     l2y = cy + bh-ch3-shadow;
810 
811     sb_box(x, l1y-shadow, w, l2y-l1y+2*shadow, vertical, z, FALSE,
812 	   !z && s->status == NAME_dragging);
813     if ( z )
814     { if ( s->status == NAME_dragging )
815       { int ew = w/2;
816 	int ex = x + (w-ew)/2;
817 	int ey = l1y + (l2y-l1y-ew)/2;
818 
819 	if ( vertical )
820 	  r_3d_ellipse(ex, ey, ew, ew, z, FALSE);
821 	else
822 	  r_3d_ellipse(ey, ex, ew, ew, z, FALSE);
823       } else if ( Repeating(s) && s->unit == NAME_line )
824       { int by = s->direction == NAME_forwards ? l2y : cy;
825 
826 	sb_box(x, by, w, l1y-cy, vertical, z, FALSE, TRUE);
827       }
828     }
829 
830     ch9 = ((ch3 * 3) / 10)+1;		/* ... and the arrows */
831     sb_triangle(x+am, l1y-1-ch9,
832 		x+w-shadow-am-1, l1y-1-ch9,
833 		x+(w-shadow)/2, cy+ch9,
834 		vertical, z,
835 		s->start != ZERO);
836     sb_triangle(x+am, l2y+1+ch9,
837 		x+w-shadow-am-1, l2y+1+ch9,
838 		x+(w-shadow)/2, cy+bh-shadow-ch9,
839 		vertical, z,
840 		valInt(s->start) + valInt(s->view) < valInt(s->length));
841 
842     if ( !z && Repeating(s) && s->unit == NAME_line )
843     { int bx = x + shadow;
844       int by = s->direction == NAME_forwards ? l2y : cy;
845       int bh = l1y-cy;
846       int bw = w - 2*shadow;
847 
848       if ( vertical )
849 	r_complement(bx, by, bw, bh);
850       else
851 	r_complement(by, bx, bh, bw);
852     }
853   }
854 
855   succeed;
856 }
857 
858 
859 static status
OpenLookEventScrollBar(ScrollBar s,EventObj ev)860 OpenLookEventScrollBar(ScrollBar s, EventObj ev)
861 { if ( isAEvent(ev, NAME_msLeftDown) )
862   { int offset = offset_event_scrollbar(s, ev);
863     int vertical = (s->orientation == NAME_vertical);
864     Int w = s->area->w;
865     Int h = s->area->h;
866     struct bubble_info button_bi;
867     compute_bubble(s, &button_bi,
868 		   BOXHEIGHT+BOX_MARGIN, BUTTON_HEIGHT*3, TRUE);
869 
870 
871     if ( offset <= button_bi.bar_start )
872     { assign(s, unit,      NAME_file);
873       assign(s, direction, NAME_goto);
874       assign(s, amount,    ZERO);
875       assign(s, status,	   NAME_topOfFile);
876       if ( vertical )
877 	changedImageGraphical(s, 0, 0, w, toInt(BOXHEIGHT));
878       else
879 	changedImageGraphical(s, 0, 0, toInt(BOXHEIGHT), h);
880     } else if ( offset >= button_bi.bar_start + button_bi.bar_length )
881     { assign(s, unit,      NAME_file);
882       assign(s, direction, NAME_goto);
883       assign(s, amount,    toInt(1000));
884       assign(s, status,	   NAME_bottomOfFile);
885       if ( vertical )
886 	changedImageGraphical(s, 0, toInt(valInt(h)-BOXHEIGHT),
887 			      w, toInt(BOXHEIGHT));
888       else
889 	changedImageGraphical(s, toInt(valInt(w)-BOXHEIGHT), 0,
890 			      toInt(BOXHEIGHT), h);
891     } else
892     { if ( offset < button_bi.start )
893       { assign(s, unit,      NAME_page);
894 	assign(s, direction, NAME_backwards);
895 	assign(s, amount,    toInt(SCROLL_PAGE_PROM));
896 	assign(s, status,    NAME_repeatDelay);
897       } else if ( offset > button_bi.start + button_bi.length )
898       { assign(s, unit,      NAME_page);
899 	assign(s, direction, NAME_forwards);
900 	assign(s, amount,    toInt(SCROLL_PAGE_PROM));
901 	assign(s, status,    NAME_repeatDelay);
902       } else if ( offset < button_bi.start + button_bi.length/3 )
903       { assign(s, unit,      NAME_line);
904 	assign(s, direction, NAME_backwards);
905 	assign(s, amount,    ONE);
906 	assign(s, status,    NAME_repeatDelay);
907       } else if ( offset > button_bi.start + (button_bi.length*2)/3 )
908       { assign(s, unit,      NAME_line);
909 	assign(s, direction, NAME_forwards);
910 	assign(s, amount,    ONE);
911 	assign(s, status,    NAME_repeatDelay);
912       } else
913       { assign(s, unit,      NAME_file);
914 	assign(s, direction, NAME_goto);
915 	assign(s, status,    NAME_dragging);
916       }
917 
918       if ( s->status == NAME_repeatDelay )
919       { LastOffset = offset;
920 	attachTimerScrollBar(s);
921       }
922 
923       changedBubbleScrollBar(s);
924     }
925   } else if ( isAEvent(ev, NAME_msLeftDrag) )
926   { if ( s->status == NAME_dragging )
927     { int offset = offset_event_scrollbar(s, ev);
928       struct bubble_info button_bi;
929       int prom;
930       int bh;
931 
932       compute_bubble(s, &button_bi,
933 		     BOXHEIGHT+BOX_MARGIN,
934 		     BUTTON_HEIGHT*3, TRUE);
935       bh = button_bi.length;
936       if ( button_bi.bar_length > bh )
937       { prom = ((offset - button_bi.bar_start - bh/2) * 1000) /
938 		   (button_bi.bar_length - bh);
939 	prom = BOUNDS(prom, 0, 1000);
940 
941 	assign(s, amount, toInt(prom));
942 	forwardScrollBar(s);
943       }
944     }
945   } else if ( isAEvent(ev, NAME_msLeftUp) )
946   { DEBUG(NAME_scrollBar, Cprintf("left-up received\n"));
947 
948     if ( (s->unit != NAME_file && s->status != NAME_repeat) ||
949 	 s->status == NAME_topOfFile ||
950 	 s->status == NAME_bottomOfFile )
951     { forwardScrollBar(s);
952       if ( instanceOfObject(s->object, ClassGraphical) )
953 	ComputeGraphical(s->object);
954       ComputeGraphical(s);
955     }
956 
957     detachTimerScrollBar(s);
958     assign(s, status, NAME_inactive);
959     changedEntireImageGraphical(s);
960 #if 0
961     if ( s->status != NAME_dragging )
962     { forwardScrollBar(s);
963       if ( instanceOfObject(s->object, ClassGraphical) )
964 	ComputeGraphical(s->object);
965       ComputeGraphical(s);
966     }
967     assign(s, status, NAME_inactive);
968     detachTimerScrollBar(s);
969     changedEntireImageGraphical(s);	/* too much, but for now ok */
970 #endif
971   } else
972     fail;				/* other button/event */
973 
974   succeed;
975 }
976 
977 #endif /*OPENLOOK*/
978 
979 #ifdef O_MOTIF
980 
981 static status
MotifEventScrollBar(ScrollBar s,EventObj ev)982 MotifEventScrollBar(ScrollBar s, EventObj ev)
983 { if ( !isAEvent(ev, NAME_button) )
984     fail;
985 
986   if ( isAEvent(ev, NAME_msLeft) )
987   { int vertical = (s->orientation == NAME_vertical);
988     int ah = ws_arrow_height_scrollbar(s);
989     Int w = s->area->w;
990     Int h = s->area->h;
991     int offset = offset_event_scrollbar(s, ev);
992     int len = (vertical ? valInt(h) : valInt(w));
993 
994     if ( ah < 0 )
995       ah = (vertical ? valInt(w) : valInt(h));
996 
997     if ( isAEvent(ev, NAME_msLeftDown) )
998     { DEBUG(NAME_comboBox, Cprintf("%s: received ms_left_down\n", pp(s)));
999 
1000       if ( offset < ah )		/* line-up */
1001       { assign(s, unit,      NAME_line);
1002 	assign(s, direction, NAME_backwards);
1003 	assign(s, amount,    ONE);
1004 	assign(s, status,    NAME_repeatDelay);
1005       } else if ( offset > len - ah )	/* line-down */
1006       { assign(s, unit,      NAME_line);
1007 	assign(s, direction, NAME_forwards);
1008 	assign(s, amount,    ONE);
1009 	assign(s, status,    NAME_repeatDelay);
1010       } else				/* not on the arrows */
1011       { struct bubble_info bi;
1012 
1013 	compute_bubble(s, &bi, ah, MIN_BUBBLE, FALSE);
1014 
1015 	if ( offset < bi.start )	/* page-up */
1016 	{ assign(s, unit,      NAME_page);
1017 	  assign(s, direction, NAME_backwards);
1018 	  assign(s, amount,    toInt(SCROLL_PAGE_PROM));
1019 	  assign(s, status,    NAME_repeatDelay);
1020 	} else if ( offset > bi.start + bi.length ) /* page-down */
1021 	{ assign(s, unit,      NAME_page);
1022 	  assign(s, direction, NAME_forwards);
1023 	  assign(s, amount,    toInt(SCROLL_PAGE_PROM));
1024 	  assign(s, status,    NAME_repeatDelay);
1025 	} else				/* on the bubble */
1026 	{ assign(s, unit,      NAME_file);
1027 	  assign(s, direction, NAME_goto);
1028 	  assign(s, amount,    promilage_event_scrollbar(s, ev));
1029 	  assign(s, offset,    toInt(offset - bi.start));
1030 	  assign(s, status,    NAME_running);
1031 	}
1032       }
1033 
1034       if ( s->status == NAME_repeatDelay )
1035       { attachTimerScrollBar(s);
1036 	changedEntireImageGraphical(s);
1037       }
1038     } else if ( isAEvent(ev, NAME_msLeftDrag) && s->status == NAME_running )
1039     { int offset = offset_event_scrollbar(s, ev);
1040       struct bubble_info bi;
1041       int prom;
1042 
1043       compute_bubble(s, &bi, ah, MIN_BUBBLE, FALSE);
1044       if ( bi.bar_length <= bi.length )
1045 	prom = 0;			/* avoid division by 0 */
1046       else
1047 	prom = ((offset - bi.bar_start - valInt(s->offset)) * 1000) /
1048 		   (bi.bar_length - bi.length);
1049       prom = BOUNDS(prom, 0, 1000);
1050 
1051       assign(s, amount, toInt(prom));
1052       forwardScrollBar(s);
1053     } else if ( isAEvent(ev, NAME_msLeftUp) )
1054     { if ( s->unit != NAME_file && s->status != NAME_repeat )
1055 	forwardScrollBar(s);
1056 
1057       assign(s, status, NAME_inactive);
1058 
1059       if ( detachTimerScrollBar(s) )
1060 	changedEntireImageGraphical(s);
1061     }
1062 
1063     succeed;
1064   }
1065 
1066   if ( isAEvent(ev, NAME_msMiddle) )
1067   { if ( isAEvent(ev, NAME_msMiddleDown) )
1068     { assign(s, unit,      NAME_file);
1069       assign(s, direction, NAME_goto);
1070       assign(s, amount,    promilage_event_scrollbar(s, ev));
1071       assign(s, status,    NAME_running);
1072 
1073       forwardScrollBar(s);
1074     } else if ( s->status == NAME_running &&
1075 		s->drag == ON &&
1076 		isAEvent(ev, NAME_msMiddleDrag) )
1077     { assign(s, amount,    promilage_event_scrollbar(s, ev));
1078       forwardScrollBar(s);
1079     } else if ( isAEvent(ev, NAME_msMiddleUp) )
1080     { assign(s, status, NAME_inactive);
1081     }
1082 
1083     succeed;
1084   }
1085 
1086   fail;
1087 }
1088 
1089 #endif /*O_MOTIF*/
1090 
1091 
1092 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1093 Scrollbar event handling.  Status field:
1094 
1095   inactive:	Doing nothing; pointer is outside the scrollbar.
1096   active:	Pointer is in the scrollbar, no button pressed.
1097   running:	A button is depressed.  Handle events to the up.
1098 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1099 
1100 static int
offset_event_scrollbar(ScrollBar s,EventObj ev)1101 offset_event_scrollbar(ScrollBar s, EventObj ev)
1102 { if ( s->orientation == NAME_horizontal )
1103     return valInt(getXEvent(ev, s));
1104   else
1105     return valInt(getYEvent(ev, s));
1106 }
1107 
1108 
1109 static Int
promilage_event_scrollbar(ScrollBar s,EventObj ev)1110 promilage_event_scrollbar(ScrollBar s, EventObj ev)
1111 { struct bubble_info bi;
1112   int ah = arrow_height_scrollbar(s);
1113   int offset = offset_event_scrollbar(s, ev);
1114   int rval;
1115 
1116   compute_bubble(s, &bi, ah, MIN_BUBBLE, FALSE);
1117   rval = ((offset - bi.bar_start) * 1000) / bi.bar_length;
1118 
1119   return toInt(BOUNDS(rval, 0, 1000));
1120 }
1121 
1122 
1123 static status
forwardScrollBar(ScrollBar s)1124 forwardScrollBar(ScrollBar s)
1125 { if ( isNil(s->message) )
1126     succeed;
1127 
1128   if ( isDefault(s->message) )
1129   { send(s->object,
1130 	 (equalName(s->orientation, NAME_horizontal)
1131 	  ? NAME_scrollHorizontal
1132 	  : NAME_scrollVertical),
1133 	 s->direction, s->unit, s->amount, EAV);
1134   } else
1135     forwardReceiverCode(s->message, s->object,
1136 			s->direction, s->unit, s->amount, EAV);
1137 
1138   succeed;
1139 }
1140 
1141 
1142 static status
updateCursorScrollBar(ScrollBar s,Name which)1143 updateCursorScrollBar(ScrollBar s, Name which)
1144 { Name name = (Name) NIL;
1145 
1146   if ( equalName(s->orientation, NAME_vertical) )
1147   { if ( equalName(which, NAME_active) )
1148       name = NAME_sbVDoubleArrow;
1149     else if ( equalName(which, NAME_up) )
1150       name = NAME_sbUpArrow;
1151     else if ( equalName(which, NAME_down) )
1152       name = NAME_sbDownArrow;
1153     else if ( equalName(which, NAME_goto) )
1154       name = NAME_sbRightArrow;
1155   } else
1156   { if ( equalName(which, NAME_active) )
1157       name = NAME_sbHDoubleArrow;
1158     else if ( equalName(which, NAME_up) )
1159       name = NAME_sbLeftArrow;
1160     else if ( equalName(which, NAME_down) )
1161       name = NAME_sbRightArrow;
1162     else if ( equalName(which, NAME_goto) )
1163       name = NAME_sbDownArrow;
1164   }
1165 
1166   send(s, NAME_cursor, name, EAV);
1167 
1168   succeed;
1169 }
1170 
1171 
1172 static status
eventScrollBar(ScrollBar s,EventObj ev)1173 eventScrollBar(ScrollBar s, EventObj ev)
1174 { if ( mapWheelMouseEvent(ev, s->object) )
1175     succeed;
1176 
1177   if ( s->look == NAME_x )
1178   { if ( equalName(s->status, NAME_inactive) )
1179     { if ( isAEvent(ev, NAME_areaEnter) )
1180       { assign(s, status,	 NAME_active);
1181 	return updateCursorScrollBar(s, NAME_active);
1182       }
1183     } else if ( equalName(s->status, NAME_active) )
1184     { if ( isDownEvent(ev) )
1185       { if ( isAEvent(ev, NAME_msLeftDown) )
1186 	{ assign(s, unit,      NAME_page);
1187 	  assign(s, direction, NAME_forwards);
1188 	  assign(s, amount,    promilage_event_scrollbar(s, ev));
1189 	  assign(s, status,    NAME_running);
1190 	  return updateCursorScrollBar(s, NAME_up);
1191 	}
1192 	if ( isAEvent(ev, NAME_msMiddleDown) )
1193 	{ assign(s, unit,      NAME_file);
1194 	  assign(s, direction, NAME_goto);
1195 	  assign(s, amount,    promilage_event_scrollbar(s, ev));
1196 	  assign(s, status,    NAME_running);
1197 	  updateCursorScrollBar(s, NAME_goto);
1198 	  forwardScrollBar(s);
1199 	  succeed;
1200 	}
1201 	if ( isAEvent(ev, NAME_msRightDown) )
1202 	{ assign(s, unit,      NAME_page);
1203 	  assign(s, direction, NAME_backwards);
1204 	  assign(s, amount,    promilage_event_scrollbar(s, ev));
1205 	  assign(s, status,    NAME_running);
1206 	  return updateCursorScrollBar(s, NAME_down);
1207 	}
1208       }
1209       if ( isAEvent(ev, NAME_areaExit) )
1210       { assign(s, status,    NAME_inactive);
1211 	return updateCursorScrollBar(s, NAME_inactive);
1212       }
1213     } else if ( equalName(s->status, NAME_running) )
1214     { if ( s->drag == ON && isAEvent(ev, NAME_msMiddleDrag) )
1215       { assign(s, amount,    promilage_event_scrollbar(s, ev));
1216 	forwardScrollBar(s);
1217 	succeed;
1218       }
1219       if ( isUpEvent(ev) )
1220       { if ( !equalName(s->unit, NAME_file) )
1221 	  forwardScrollBar(s);
1222 
1223 	if ( allButtonsUpEvent(ev) )
1224 	{ if ( insideEvent(ev, (Graphical) s) )
1225 	  { assign(s, status, NAME_active);
1226 	    return updateCursorScrollBar(s, NAME_active);
1227 	  }
1228 	  assign(s, status, NAME_inactive);
1229 	  return updateCursorScrollBar(s, NAME_inactive);
1230 	}
1231       }
1232     }
1233   } else if ( s->look == NAME_openLook )
1234   { if ( OpenLookEventScrollBar(s, ev) )
1235       succeed;
1236   } else if ( s->look == NAME_motif ||
1237 	      s->look == NAME_gtk ||
1238 	      s->look == NAME_win )
1239   { if ( MotifEventScrollBar(s, ev) )
1240       succeed;
1241   }
1242 
1243   return eventGraphical(s, ev);
1244 }
1245 
1246 
1247 static status
orientationScrollBar(ScrollBar s,Name or)1248 orientationScrollBar(ScrollBar s, Name or)
1249 { if ( s->orientation == or )
1250     succeed;
1251 
1252   CHANGING_GRAPHICAL(s,
1253 	swapInt(s->area->h, s->area->w);
1254 	assign(s, orientation, or);
1255 	changedEntireImageGraphical(s));
1256 
1257   succeed;
1258 }
1259 
1260 
1261 static status
viewScrollBar(ScrollBar s,Int n)1262 viewScrollBar(ScrollBar s, Int n)
1263 { if (valInt(n) < 0)
1264     n = ZERO;
1265 
1266   if ( s->view != n )
1267   { assign(s, view, n);
1268     requestComputeGraphical(s, DEFAULT);
1269   }
1270 
1271   succeed;
1272 }
1273 
1274 
1275 static status
startScrollBar(ScrollBar s,Int n)1276 startScrollBar(ScrollBar s, Int n)
1277 { if (valInt(n) < 0)
1278     n = ZERO;
1279 
1280   if ( s->start != n )
1281   { assign(s, start, n);
1282     requestComputeGraphical(s, DEFAULT);
1283   }
1284 
1285   succeed;
1286 }
1287 
1288 
1289 static status
lengthScrollBar(ScrollBar s,Int n)1290 lengthScrollBar(ScrollBar s, Int n)
1291 { if (valInt(n) < 0)
1292     n = ZERO;
1293 
1294   if ( s->length != n )
1295   { assign(s, length, n);
1296     requestComputeGraphical(s, DEFAULT);
1297   }
1298 
1299   succeed;
1300 }
1301 
1302 
1303 status
bubbleScrollBar(ScrollBar sb,Int l,Int s,Int v)1304 bubbleScrollBar(ScrollBar sb, Int l, Int s, Int v)
1305 { if ( valInt(l) < 0 ) l = ZERO;
1306   if ( valInt(s) < 0 ) s = ZERO;
1307   if ( valInt(v) < 0 ) v = ZERO;
1308 
1309   if ( sb->length == l && sb->start == s && sb->view == v )
1310     succeed;
1311 
1312   DEBUG(NAME_scroll, Cprintf("bubbleScrollBar(%s, %d, %d, %d)\n",
1313 			     pp(sb), valInt(l), valInt(s), valInt(v)));
1314 
1315   assign(sb, length, l);
1316   assign(sb, start,  s);
1317   assign(sb, view,   v);
1318 
1319   if ( sb->auto_hide == ON &&
1320        hasSendMethodObject(sb->object, NAME_showScrollBar) )
1321   { if ( s == ZERO && valInt(v) >= valInt(l) ) /* all is shown */
1322     { if ( sb->displayed == ON )
1323       { if ( send(sb->object, NAME_showScrollBar, OFF, sb, EAV) )
1324 	  succeed;
1325       }
1326     } else
1327     { if ( sb->displayed == OFF )
1328       { send(sb->object, NAME_showScrollBar, ON, sb, EAV);
1329       }
1330     }
1331   }
1332 
1333   return requestComputeGraphical(sb, DEFAULT);
1334 }
1335 
1336 
1337 static status
autoHideScrollBar(ScrollBar sb,BoolObj val)1338 autoHideScrollBar(ScrollBar sb, BoolObj val)
1339 { if ( sb->auto_hide != val )
1340   { assign(sb, auto_hide, val);
1341     requestComputeGraphical(sb, DEFAULT);
1342   }
1343 
1344   succeed;
1345 }
1346 
1347 
1348 static status
lookScrollBar(ScrollBar s,Name look)1349 lookScrollBar(ScrollBar s, Name look)
1350 { CHANGING_GRAPHICAL(s,
1351 		     assign(s, look, look);
1352 		     assign(s, distance, toInt(look == NAME_x ? -1 : 1));
1353 		     changedEntireImageGraphical(s));
1354 
1355   succeed;
1356 }
1357 
1358 
1359 static status
convertLoadedObjectScrollBar(ScrollBar sb,Int ov,Int nv)1360 convertLoadedObjectScrollBar(ScrollBar sb, Int ov, Int nv)
1361 { if ( isName(sb->placement) )
1362   { Chain ch = newObject(ClassChain, EAV);
1363     static char *names[] = {"left", "right", "top", "bottom"};
1364     int i;
1365 
1366     for(i=0; i<4; i++)
1367     { Name place = CtoKeyword(names[i]);
1368 
1369       if ( send(sb->placement, NAME_sub, place, ON, EAV) )
1370 	appendChain(ch, place);
1371     }
1372     assign(sb, placement, ch);
1373   }
1374 
1375   succeed;
1376 }
1377 
1378 
1379 		 /*******************************
1380 		 *	 CLASS DECLARATION	*
1381 		 *******************************/
1382 
1383 /* Type declarations */
1384 
1385 static char *T_convertLoadedObject[] =
1386         { "int", "int" };
1387 static char *T_bubble[] =
1388         { "length=int", "start=int", "view=int" };
1389 static char *T_initialise[] =
1390         { "object=object", "orientation={horizontal,vertical}", "message=[code]*" };
1391 
1392 /* Instance Variables */
1393 
1394 static vardecl var_scrollBar[] =
1395 { IV(NAME_message, "[code]*", IV_BOTH,
1396      NAME_action, "Message used to inform object"),
1397   IV(NAME_object, "graphical*", IV_BOTH,
1398      NAME_client, "Graphical to be scrolled scrolled"),
1399   IV(NAME_placement, "chain", IV_GET,
1400      NAME_layout, "Relative automatic placement"),
1401   IV(NAME_distance, "int", IV_GET,
1402      NAME_layout, "Relative distance (pixels)"),
1403   IV(NAME_status, "name", IV_GET,
1404      NAME_event, "Current status for event parsing"),
1405   SV(NAME_orientation, "{horizontal,vertical}", IV_GET|IV_STORE, orientationScrollBar,
1406      NAME_appearance, "Scroll object horizontal or vertical"),
1407   SV(NAME_view, "int", IV_GET|IV_STORE, viewScrollBar,
1408      NAME_scroll, "Length of visible part"),
1409   SV(NAME_start, "int", IV_GET|IV_STORE, startScrollBar,
1410      NAME_scroll, "Start of visible part"),
1411   SV(NAME_length, "int", IV_GET|IV_STORE, lengthScrollBar,
1412      NAME_scroll, "Total length of object"),
1413   IV(NAME_bubbleStart, "int", IV_NONE,
1414      NAME_internal, "Pixel position of bubble"),
1415   IV(NAME_bubbleLength, "int", IV_NONE,
1416      NAME_internal, "Pixel size of bubble"),
1417   SV(NAME_look, "{x,open_look,motif,win,gtk}", IV_GET|IV_STORE, lookScrollBar,
1418      NAME_appearance, "Look-and-feel (only `x')"),
1419   IV(NAME_drag, "bool", IV_BOTH,
1420      NAME_event, "If @on, messages are sent continuously"),
1421   IV(NAME_amount, "int", IV_NONE,
1422      NAME_internal, "Amount to scroll"),
1423   IV(NAME_direction, "{forwards,backwards,goto}", IV_NONE,
1424      NAME_internal, "Direction in which to scroll or jump"),
1425   IV(NAME_unit, "{line,page,file}", IV_NONE,
1426      NAME_internal, "Unit to scroll"),
1427   IV(NAME_offset, "int", IV_NONE,
1428      NAME_internal, "Offset of down from top of bubble"),
1429   SV(NAME_autoHide, "bool", IV_GET|IV_STORE, autoHideScrollBar,
1430      NAME_internal, "If @on, hide if all is visible")
1431 };
1432 
1433 /* Send Methods */
1434 
1435 static senddecl send_scrollBar[] =
1436 { SM(NAME_compute, 0, NULL, computeScrollBar,
1437      DEFAULT, "Recompute the scrollbar values"),
1438   SM(NAME_convertLoadedObject, 2, T_convertLoadedObject, convertLoadedObjectScrollBar,
1439      DEFAULT, "Convert placement attribute"),
1440   SM(NAME_event, 1, "event", eventScrollBar,
1441      DEFAULT, "Process a user event"),
1442   SM(NAME_initialise, 3, T_initialise, initialiseScrollBar,
1443      DEFAULT, "Create from object, orientation and message"),
1444   SM(NAME_unlink, 0, NULL, unlinkScrollBar,
1445      DEFAULT, "Stop/disconnect repeat timer"),
1446   SM(NAME_place, 1, "[graphical]", placeScrollBar,
1447      NAME_area, "Position scrollbar relative to object"),
1448   SM(NAME_bubble, 3, T_bubble, bubbleScrollBar,
1449      NAME_scroll, "Set length, start and view"),
1450   SM(NAME_repeat, 0, NULL, repeatScrollBar,
1451      NAME_scroll, "Repeat last action (->look: open_look)")
1452 };
1453 
1454 /* Get Methods */
1455 
1456 #define get_scrollBar NULL
1457 /*
1458 static getdecl get_scrollBar[] =
1459 {
1460 };
1461 */
1462 
1463 /* Resources */
1464 
1465 static classvardecl rc_scrollBar[] =
1466 { RC(NAME_background, "[elevation|colour|pixmap]",
1467 /*   UXWIN("@_dialog_bg", "win_window"), */
1468      UXWIN("elevation(@nil, -1, grey66)", "win_window"),
1469      "Colour of background parts"),
1470   RC(NAME_colour, "[colour]", UXWIN("@_dialog_bg", "win_btnface"),
1471      "Colour of foreground parts"),
1472   RC(NAME_distance, "int", UXWIN("2", "0"),
1473      "Distance to graphical"),
1474   RC(NAME_elevation, "elevation*",
1475      UXWIN("elevation(@nil, 1, @_dialog_bg)",
1476 	   "elevation(@nil, 2, win_menu)"),
1477      "3-D effect elevation"),
1478   RC(NAME_look, "{x,open_look,motif,gtk,win}", UXWIN("gtk", "win"),
1479      "Look-and-feel"),
1480   RC(NAME_pen, "int", UXWIN("@_win_pen", "0"),
1481      "Thickness of surrounding box"),
1482   RC(NAME_placement, "chain", "[right,bottom]",
1483      "Relative placement"),
1484   RC(NAME_repeatDelay, "real", "0.35",
1485      "OpenLook: time to wait until start of repeat"),
1486   RC(NAME_repeatInterval, "real", "0.06",
1487      "OpenLook: interval between repeats"),
1488   RC(NAME_width, "[int]", UXWIN("18", "@default"),
1489      "Width of the scroll_bar"),
1490   RC(NAME_autoHide, "bool", "@on",
1491      "Automatically hide bar if all is shown"),
1492   RC(NAME_cursor, "cursor", UXWIN("top_left_arrow", "win_arrow"), NULL)
1493 };
1494 
1495 /* Class Declaration */
1496 
1497 static Name scrollBar_termnames[] =
1498 	{ NAME_object, NAME_orientation, NAME_message };
1499 
1500 ClassDecl(scrollBar_decls,
1501           var_scrollBar, send_scrollBar, get_scrollBar, rc_scrollBar,
1502           3, scrollBar_termnames,
1503           "$Rev$");
1504 
1505 
1506 status
makeClassScrollBar(Class class)1507 makeClassScrollBar(Class class)
1508 { declareClass(class, &scrollBar_decls);
1509   setRedrawFunctionClass(class, RedrawAreaScrollBar);
1510 
1511   succeed;
1512 }
1513