1 /*
2  * Copyright 2004-2008 James Bursa <bursa@users.sourceforge.net>
3  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
4  * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net>
5  *
6  * This file is part of NetSurf, http://www.netsurf-browser.org/
7  *
8  * NetSurf is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * NetSurf is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /**
22  * \file
23  * implementation of scrollbar widget.
24  */
25 
26 #include <stdbool.h>
27 #include <stdlib.h>
28 
29 #include "utils/log.h"
30 #include "utils/messages.h"
31 #include "utils/utils.h"
32 #include "utils/nscolour.h"
33 #include "utils/nsoption.h"
34 #include "netsurf/browser_window.h"
35 #include "netsurf/mouse.h"
36 #include "netsurf/plotters.h"
37 
38 #include "desktop/system_colour.h"
39 #include "desktop/scrollbar.h"
40 
41 /**
42  * Scrollbar context
43  */
44 struct scrollbar {
45 	/** Horizontal scrollbar if true, else vertical */
46 	bool horizontal;
47 	/** Length of the scrollbar widget */
48 	int length;
49 
50 	/** Length of the full scrollable area */
51 	int full_size;
52 	/** Length visible part of the scrollable area */
53 	int visible_size;
54 
55 	/** Current scroll offset to visible area */
56 	int offset;
57 
58 	/** Position of the scrollbar */
59 	int bar_pos;
60 	/** Length of the scrollbar */
61 	int bar_len;
62 
63 	/** Callback receiving scrollbar events */
64 	scrollbar_client_callback client_callback;
65 	/** User data passed to the callback */
66 	void *client_data;
67 
68 	/** Flag indicating drag at progess */
69 	bool dragging;
70 	/** Coordinate value at drag start */
71 	int drag_start_coord;
72 	/** Scrollbar offset or bar_pos at drag start */
73 	int drag_start_pos;
74 	/** Flag indicating that the drag corresponds to a dragged
75 	 * content area, rather than a dragged scrollbar.
76 	 */
77 	bool drag_content;
78 
79 	/** Parpendicular scrollbar, or NULL */
80 	struct scrollbar *pair;
81 	/** Flag indicating that the current drag affects the
82 	 *  perpendicular scrollbar too
83 	 */
84 	bool pair_drag;
85 };
86 
87 
88 
89 /*
90  * Exported interface.  Documented in desktop/scrollbar.h
91  */
92 nserror
scrollbar_create(bool horizontal,int length,int full_size,int visible_size,void * client_data,scrollbar_client_callback client_callback,struct scrollbar ** s)93 scrollbar_create(bool horizontal,
94 		 int length,
95 		 int full_size,
96 		 int visible_size,
97 		 void *client_data,
98 		 scrollbar_client_callback client_callback,
99 		 struct scrollbar **s)
100 {
101 	struct scrollbar *scrollbar;
102 	int well_length;
103 
104 	scrollbar = malloc(sizeof(struct scrollbar));
105 	if (scrollbar == NULL) {
106 		*s = NULL;
107 		return NSERROR_NOMEM;
108 	}
109 
110 	scrollbar->horizontal = horizontal;
111 	scrollbar->length = length;
112 	scrollbar->full_size = full_size;
113 	scrollbar->visible_size = visible_size;
114 	scrollbar->offset = 0;
115 	scrollbar->bar_pos = 0;
116 	scrollbar->pair = NULL;
117 	scrollbar->pair_drag = false;
118 
119 	well_length = length - 2 * SCROLLBAR_WIDTH;
120 	scrollbar->bar_len = (full_size == 0) ? 0 :
121 		((well_length * visible_size) / full_size);
122 
123 	scrollbar->client_callback = client_callback;
124 	scrollbar->client_data = client_data;
125 
126 	scrollbar->dragging = false;
127 	scrollbar->drag_content = false;
128 
129 	*s = scrollbar;
130 
131 	return NSERROR_OK;
132 }
133 
134 
135 /*
136  * Exported interface.  Documented in scrollbar.h
137  */
scrollbar_destroy(struct scrollbar * s)138 void scrollbar_destroy(struct scrollbar *s)
139 {
140 	if (s->pair != NULL) {
141 		s->pair->pair = NULL;
142 	}
143 	free(s);
144 }
145 
146 
147 /**
148  * Draw an outline rectangle common to several scrollbar elements.
149  *
150  * \param ctx current redraw context
151  * \param area the area of the scrollbar
152  * \param c base colour of the outline, the other colours are created by
153  *		lightening or darkening this one
154  * \param inset true for inset outline, false for an outset one
155  * \return NSERROR_OK on success else error code
156  */
157 static inline nserror
scrollbar_rectangle(const struct redraw_context * ctx,struct rect * area,colour c,bool inset)158 scrollbar_rectangle(const struct redraw_context *ctx,
159 		    struct rect *area,
160 		    colour c,
161 		    bool inset)
162 {
163 	struct rect line;
164 	nserror res;
165 
166 	static plot_style_t c0 = {
167 		.stroke_type = PLOT_OP_TYPE_SOLID,
168 		.stroke_width = plot_style_int_to_fixed(1),
169 	};
170 
171 	static plot_style_t c1 = {
172 		.stroke_type = PLOT_OP_TYPE_SOLID,
173 		.stroke_width = plot_style_int_to_fixed(1),
174 	};
175 
176 	static plot_style_t c2 = {
177 		.stroke_type = PLOT_OP_TYPE_SOLID,
178 		.stroke_width = plot_style_int_to_fixed(1),
179 	};
180 
181 	if (inset) {
182 		c0.stroke_colour = darken_colour(c);
183 		c1.stroke_colour = lighten_colour(c);
184 	} else {
185 		c0.stroke_colour = lighten_colour(c);
186 		c1.stroke_colour = darken_colour(c);
187 	}
188 	c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour);
189 
190 	/* Plot the outline */
191 
192 	line.x0 = area->x0; line.y0 = area->y0;
193 	line.x1 = area->x1; line.y1 = area->y0;
194 	res = ctx->plot->line(ctx, &c0, &line);
195 	if (res != NSERROR_OK) {
196 		return res;
197 	}
198 
199 	line.x0 = area->x1; line.y0 = area->y0;
200 	line.x1 = area->x1; line.y1 = area->y1 + 1;
201 	res = ctx->plot->line(ctx, &c1, &line);
202 	if (res != NSERROR_OK) {
203 		return res;
204 	}
205 
206 	line.x0 = area->x1; line.y0 = area->y0;
207 	line.x1 = area->x1; line.y1 = area->y0 + 1;
208 	res = ctx->plot->line(ctx, &c2, &line);
209 	if (res != NSERROR_OK) {
210 		return res;
211 	}
212 
213 	line.x0 = area->x1; line.y0 = area->y1;
214 	line.x1 = area->x0; line.y1 = area->y1;
215 	res = ctx->plot->line(ctx, &c1, &line);
216 	if (res != NSERROR_OK) {
217 		return res;
218 	}
219 
220 	line.x0 = area->x0; line.y0 = area->y1;
221 	line.x1 = area->x0; line.y1 = area->y0;
222 	res = ctx->plot->line(ctx, &c0, &line);
223 	if (res != NSERROR_OK) {
224 		return res;
225 	}
226 
227 	line.x0 = area->x0; line.y0 = area->y1;
228 	line.x1 = area->x0; line.y1 = area->y1 + 1;
229 	res = ctx->plot->line(ctx, &c2, &line);
230 
231 	return res;
232 }
233 
234 
235 /*
236  * Exported interface.  Documented in scrollbar.h
237  */
238 nserror
scrollbar_redraw(struct scrollbar * s,int x,int y,const struct rect * clip,float scale,const struct redraw_context * ctx)239 scrollbar_redraw(struct scrollbar *s,
240 		 int x, int y,
241 		 const struct rect *clip,
242 		 float scale,
243 		 const struct redraw_context *ctx)
244 {
245 	int w = SCROLLBAR_WIDTH;
246 	int bar_pos, bar_c0, bar_c1;
247 	int v[6]; /* array of triangle vertices */
248 	struct rect area;
249 	struct rect rect;
250 	nserror res;
251 
252 	plot_style_t bg_fill_style = {
253 		.fill_type = PLOT_OP_TYPE_SOLID,
254 		.fill_colour = nscolours[NSCOLOUR_SCROLL_WELL],
255 	};
256 	plot_style_t fg_fill_style = {
257 		.fill_type = PLOT_OP_TYPE_SOLID,
258 		.fill_colour = nscolours[NSCOLOUR_BUTTON_BG],
259 	};
260 	plot_style_t arrow_fill_style = {
261 		.fill_type = PLOT_OP_TYPE_SOLID,
262 		.fill_colour = nscolours[NSCOLOUR_BUTTON_FG],
263 	};
264 
265 	area.x0 = x;
266 	area.y0 = y;
267 	area.x1 = x + (s->horizontal ? s->length : SCROLLBAR_WIDTH) - 1;
268 	area.y1 = y + (s->horizontal ? SCROLLBAR_WIDTH : s->length) - 1;
269 	bar_pos = s->bar_pos;
270 	bar_c1 = (s->horizontal ? area.x0 : area.y0) + SCROLLBAR_WIDTH +
271 		s->bar_pos + s->bar_len - 1;
272 
273 	if (scale != 1.0) {
274 		w *= scale;
275 		area.x0 *= scale;
276 		area.y0 *= scale;
277 		area.x1 *= scale;
278 		area.y1 *= scale;
279 		bar_pos *= scale;
280 		bar_c1 *= scale;
281 	}
282 
283 	bar_c0 = (s->horizontal ? area.x0 : area.y0) + w + bar_pos;
284 
285 	/* if scrollbar is outside the clipping rectangle, nothing to render */
286 	if ((area.x1 < clip->x0) ||
287 	    (area.y1 < clip->y0) ||
288 	    (clip->x1 < area.x0) ||
289 	    (clip->y1 < area.y0)) {
290 		return NSERROR_OK;
291 	}
292 
293 	if (s->horizontal) {
294 		/* scrollbar is horizontal */
295 
296 		/* scrollbar outline */
297 		res = scrollbar_rectangle(ctx, &area,
298 					  bg_fill_style.fill_colour, true);
299 		if (res != NSERROR_OK) {
300 			return res;
301 		}
302 
303 		/* left arrow icon border */
304 		rect.x0 = area.x0 + 1;
305 		rect.y0 = area.y0 + 1;
306 		rect.x1 = area.x0 + w - 2;
307 		rect.y1 = area.y1 - 1;
308 		res = scrollbar_rectangle(ctx, &rect,
309 					  fg_fill_style.fill_colour, false);
310 		if (res != NSERROR_OK) {
311 			return res;
312 		}
313 
314 		/* left arrow icon background */
315 		rect.x0 = area.x0 + 2;
316 		rect.y0 = area.y0 + 2;
317 		rect.x1 = area.x0 + w - 2;
318 		rect.y1 = area.y1 - 1;
319 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
320 		if (res != NSERROR_OK) {
321 			return res;
322 		}
323 
324 		/* left arrow */
325 		v[0] = area.x0 + w / 4;
326 		v[1] = area.y0 + w / 2;
327 		v[2] = area.x0 + w * 3 / 4;
328 		v[3] = area.y0 + w / 4;
329 		v[4] = area.x0 + w * 3 / 4;
330 		v[5] = area.y0 + w * 3 / 4;
331 		res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3);
332 		if (res != NSERROR_OK) {
333 			return res;
334 		}
335 
336 		/* scrollbar well background */
337 		rect.x0 = area.x0 + w - 1;
338 		rect.y0 = area.y0 + 1;
339 		rect.x1 = area.x1 - w + 2;
340 		rect.y1 = area.y1;
341 		res = ctx->plot->rectangle(ctx, &bg_fill_style, &rect);
342 		if (res != NSERROR_OK) {
343 			return res;
344 		}
345 
346 		/* scrollbar position indicator bar */
347 		rect.x0 = bar_c0;
348 		rect.y0 = area.y0 + 1;
349 		rect.x1 = bar_c1;
350 		rect.y1 = area.y1 - 1;
351 		res = scrollbar_rectangle(ctx, &rect,
352 					  fg_fill_style.fill_colour, false);
353 		if (res != NSERROR_OK) {
354 			return res;
355 		}
356 
357 		rect.x0 = bar_c0 + 1;
358 		rect.y0 = area.y0 + 2;
359 		rect.x1 = bar_c1;
360 		rect.y1 = area.y1 - 1;
361 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
362 		if (res != NSERROR_OK) {
363 			return res;
364 		}
365 
366 		/* right arrow icon border */
367 		rect.x0 = area.x1 - w + 2;
368 		rect.y0 = area.y0 + 1;
369 		rect.x1 = area.x1 - 1;
370 		rect.y1 = area.y1 - 1;
371 		res = scrollbar_rectangle(ctx, &rect,
372 					  fg_fill_style.fill_colour, false);
373 		if (res != NSERROR_OK) {
374 			return res;
375 		}
376 
377 		/* right arrow icon background */
378 		rect.x0 = area.x1 - w + 3;
379 		rect.y0 = area.y0 + 2;
380 		rect.x1 = area.x1 - 1;
381 		rect.y1 = area.y1 - 1;
382 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
383 		if (res != NSERROR_OK) {
384 			return res;
385 		}
386 
387 		/* right arrow */
388 		v[0] = rect.x1 - w / 4 + 1;
389 		v[1] = rect.y0 + w / 2;
390 		v[2] = rect.x1 - w * 3 / 4 + 1;
391 		v[3] = rect.y0 + w / 4;
392 		v[4] = rect.x1 - w * 3 / 4 + 1;
393 		v[5] = rect.y0 + w * 3 / 4;
394 		res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3);
395 		if (res != NSERROR_OK) {
396 			return res;
397 		}
398 	} else {
399 		/* scrollbar is vertical */
400 
401 		/* outline */
402 		res = scrollbar_rectangle(ctx, &area,
403 					  bg_fill_style.fill_colour, true);
404 		if (res != NSERROR_OK) {
405 			return res;
406 		}
407 
408 		/* top arrow border */
409 		rect.x0 = area.x0 + 1;
410 		rect.y0 = area.y0 + 1;
411 		rect.x1 = area.x1 - 1;
412 		rect.y1 = area.y0 + w - 2;
413 		res = scrollbar_rectangle(ctx, &rect,
414 					  fg_fill_style.fill_colour, false);
415 		if (res != NSERROR_OK) {
416 			return res;
417 		}
418 
419 		/* top arrow background */
420 		rect.x0 = area.x0 + 2;
421 		rect.y0 = area.y0 + 2;
422 		rect.x1 = area.x1 - 1;
423 		rect.y1 = area.y0 + w - 2;
424 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
425 		if (res != NSERROR_OK) {
426 			return res;
427 		}
428 
429 		/* up arrow */
430 		v[0] = area.x0 + w / 2;
431 		v[1] = area.y0 + w / 4;
432 		v[2] = area.x0 + w / 4;
433 		v[3] = area.y0 + w * 3 / 4;
434 		v[4] = area.x0 + w * 3 / 4;
435 		v[5] = area.y0 + w * 3 / 4;
436 		res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3);
437 		if (res != NSERROR_OK) {
438 			return res;
439 		}
440 
441 		/* scrollbar well background */
442 		rect.x0 = area.x0 + 1;
443 		rect.y0 = area.y0 + w - 1;
444 		rect.x1 = area.x1;
445 		rect.y1 = area.y1 - w + 2;
446 		res = ctx->plot->rectangle(ctx, &bg_fill_style, &rect);
447 		if (res != NSERROR_OK) {
448 			return res;
449 		}
450 
451 		/* scrollbar position indicator bar */
452 		rect.x0 = area.x0 + 1;
453 		rect.y0 = bar_c0;
454 		rect.x1 = area.x1 - 1;
455 		rect.y1 = bar_c1;
456 		res = scrollbar_rectangle(ctx, &rect,
457 					  fg_fill_style.fill_colour, false);
458 		if (res != NSERROR_OK) {
459 			return res;
460 		}
461 
462 		rect.x0 = area.x0 + 2;
463 		rect.y0 = bar_c0 + 1;
464 		rect.x1 = area.x1 - 1;
465 		rect.y1 = bar_c1;
466 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
467 		if (res != NSERROR_OK) {
468 			return res;
469 		}
470 
471 		/* down arrow icon border */
472 		rect.x0 = area.x0 + 1;
473 		rect.y0 = area.y1 - w + 2;
474 		rect.x1 = area.x1 - 1;
475 		rect.y1 = area.y1 - 1;
476 		res = scrollbar_rectangle(ctx, &rect,
477 					  fg_fill_style.fill_colour, false);
478 		if (res != NSERROR_OK) {
479 			return res;
480 		}
481 
482 		/* down arrow icon background */
483 		rect.x0 = area.x0 + 2;
484 		rect.y0 = area.y1 - w + 3;
485 		rect.x1 = area.x1 - 1;
486 		rect.y1 = area.y1 - 1;
487 		res = ctx->plot->rectangle(ctx, &fg_fill_style, &rect);
488 		if (res != NSERROR_OK) {
489 			return res;
490 		}
491 
492 		/* down arrow */
493 		v[0] = area.x0 + w / 2;
494 		v[1] = area.y1 - w / 4 + 1;
495 		v[2] = area.x0 + w / 4;
496 		v[3] = area.y1 - w * 3 / 4 + 1;
497 		v[4] = area.x0 + w * 3 / 4;
498 		v[5] = area.y1 - w * 3 / 4 + 1;
499 		res = ctx->plot->polygon(ctx, &arrow_fill_style, v, 3);
500 		if (res != NSERROR_OK) {
501 			return res;
502 		}
503 	}
504 
505 	return NSERROR_OK;
506 }
507 
508 
509 /*
510  * Exported interface.  Documented in scrollbar.h
511  */
scrollbar_set(struct scrollbar * s,int value,bool bar_pos)512 void scrollbar_set(struct scrollbar *s, int value, bool bar_pos)
513 {
514 	int well_length;
515 	int old_offset = s->offset;
516 	struct scrollbar_msg_data msg;
517 
518 	if (value < 0) {
519 		value = 0;
520 	}
521 
522 	if (s->full_size == s->visible_size) {
523 		return;
524 	}
525 
526 	well_length = s->length - 2 * SCROLLBAR_WIDTH;
527 	if (bar_pos) {
528 		if (value > well_length - s->bar_len) {
529 			s->bar_pos = well_length - s->bar_len;
530 		} else {
531 			s->bar_pos = value;
532 		}
533 
534 		s->offset = ((well_length - s->bar_len) < 1) ? 0 :
535 			(((s->full_size - s->visible_size) *
536 			  s->bar_pos) / (well_length - s->bar_len));
537 
538 	} else {
539 		if (value > s->full_size - s->visible_size) {
540 			s->offset = s->full_size - s->visible_size;
541 		} else {
542 			s->offset = value;
543 		}
544 
545 		s->bar_pos = (s->full_size < 1) ? 0 :
546 			((well_length * s->offset) / s->full_size);
547 	}
548 
549 	if (s->offset != old_offset) {
550 		/* client callback if there was a chnage */
551 		msg.scrollbar = s;
552 		msg.msg = SCROLLBAR_MSG_MOVED;
553 		msg.scroll_offset = s->offset;
554 		s->client_callback(s->client_data, &msg);
555 	}
556 }
557 
558 
559 /*
560  * Exported interface.  Documented in scrollbar.h
561  */
scrollbar_scroll(struct scrollbar * s,int change)562 bool scrollbar_scroll(struct scrollbar *s, int change)
563 {
564 	int well_length;
565 	int old_offset = s->offset;
566 	struct scrollbar_msg_data msg;
567 
568 	if (change == 0 || s->full_size <= s->visible_size) {
569 		/* zero scroll step, or unscrollable */
570 		return false;
571 	}
572 
573 	/* Convert named change values to appropriate pixel offset value */
574 	switch (change) {
575 	case SCROLL_TOP:
576 		change = -s->full_size;
577 		break;
578 
579 	case SCROLL_PAGE_UP:
580 		change = -s->visible_size;
581 		break;
582 
583 	case SCROLL_PAGE_DOWN:
584 		change = s->visible_size;
585 		break;
586 
587 	case SCROLL_BOTTOM:
588 		change = s->full_size;
589 		break;
590 
591 	default:
592 		/* Change value is already a pixel offset */
593 		break;
594 	}
595 
596 	/* Get new offset */
597 	if (s->offset + change > s->full_size - s->visible_size) {
598 		s->offset = s->full_size - s->visible_size;
599 	} else if (s->offset + change < 0) {
600 		s->offset = 0;
601 	} else {
602 		s->offset += change;
603 	}
604 
605 	if (s->offset == old_offset) {
606 		/* Nothing happened */
607 		return false;
608 	}
609 
610 	/* Update scrollbar */
611 	well_length = s->length - 2 * SCROLLBAR_WIDTH;
612 	s->bar_pos = (s->full_size < 1) ? 0 :
613 		((well_length * s->offset) / s->full_size);
614 
615 	msg.scrollbar = s;
616 	msg.msg = SCROLLBAR_MSG_MOVED;
617 	msg.scroll_offset = s->offset;
618 	s->client_callback(s->client_data, &msg);
619 
620 	return true;
621 }
622 
623 
624 /*
625  * Exported interface.  Documented in scrollbar.h
626  */
scrollbar_get_offset(struct scrollbar * s)627 int scrollbar_get_offset(struct scrollbar *s)
628 {
629 	if (s == NULL) {
630 		return 0;
631 	}
632 	return s->offset;
633 }
634 
635 
636 /*
637  * Exported interface.  Documented in scrollbar.h
638  */
scrollbar_set_extents(struct scrollbar * s,int length,int visible_size,int full_size)639 void scrollbar_set_extents(struct scrollbar *s, int length,
640 			   int visible_size, int full_size)
641 {
642 	int cur_excess = s->full_size - s->visible_size;
643 	int well_length;
644 	struct scrollbar_msg_data msg;
645 
646 	if (length == s->length &&
647 	    visible_size == s->visible_size &&
648 	    full_size == s->full_size) {
649 		/* Nothing's changed. */
650 		return;
651 	}
652 
653 	if (length != -1) {
654 		s->length = length;
655 	}
656 	if (visible_size != -1) {
657 		s->visible_size = visible_size;
658 	}
659 	if (full_size != -1) {
660 		s->full_size = full_size;
661 	}
662 
663 	if (s->full_size < s->visible_size) {
664 		s->full_size = s->visible_size;
665 	}
666 
667 	/* Update scroll offset (scaled in proportion with change in excess) */
668 	if (cur_excess <= 0) {
669 		s->offset = 0;
670 	} else {
671 		s->offset = (full_size - visible_size) * s->offset / cur_excess;
672 	}
673 
674 	well_length = s->length - 2 * SCROLLBAR_WIDTH;
675 
676 	if (s->full_size < 1) {
677 		s->bar_len = well_length;
678 		s->bar_pos = 0;
679 	} else {
680 		s->bar_len = (well_length * s->visible_size) / s->full_size;
681 		s->bar_pos = (well_length * s->offset) / s->full_size;
682 	}
683 
684 	msg.scrollbar = s;
685 	msg.msg = SCROLLBAR_MSG_MOVED;
686 	msg.scroll_offset = s->offset;
687 	s->client_callback(s->client_data, &msg);
688 }
689 
690 
691 /*
692  * Exported interface.  Documented in scrollbar.h
693  */
scrollbar_is_horizontal(struct scrollbar * s)694 bool scrollbar_is_horizontal(struct scrollbar *s)
695 {
696 	return s->horizontal;
697 }
698 
699 
700 /**
701  * Internal procedure used for starting a drag scroll for a scrollbar.
702  *
703  * \param s		the scrollbar to start the drag for
704  * \param x		the X coordinate of the drag start
705  * \param y		the Y coordinate of the drag start
706  * \param content_drag	whether this should be a reverse drag (used when the
707  *			user drags the content area, rather than the scrollbar)
708  * \param pair		whether the drag is a '2D' scroll
709  */
710 static void
scrollbar_drag_start_internal(struct scrollbar * s,int x,int y,bool content_drag,bool pair)711 scrollbar_drag_start_internal(struct scrollbar *s,
712 			      int x, int y,
713 			      bool content_drag,
714 			      bool pair)
715 {
716 	struct scrollbar_msg_data msg;
717 
718 	s->drag_start_coord = s->horizontal ? x : y;
719 	s->drag_start_pos = (content_drag) ? s->offset : s->bar_pos;
720 
721 	s->dragging = true;
722 	s->drag_content = content_drag;
723 
724 	msg.scrollbar = s;
725 
726 	/** \todo some proper numbers please! */
727 	if (s->horizontal) {
728 		msg.x0 = -2048;
729 		msg.x1 = 2048;
730 		msg.y0 = 0;
731 		msg.y1 = 0;
732 	} else {
733 		msg.x0 = 0;
734 		msg.x1 = 0;
735 		msg.y0 = -2048;
736 		msg.y1 = 2048;
737 	}
738 
739 	if (pair && s->pair != NULL) {
740 		s->pair_drag = true;
741 
742 		s->pair->drag_start_coord =
743 			s->pair->horizontal ? x : y;
744 
745 		s->pair->drag_start_pos = (content_drag) ? s->pair->offset :
746 			s->pair->bar_pos;
747 
748 		s->pair->dragging = true;
749 		s->pair->drag_content = content_drag;
750 
751 		if (s->pair->horizontal) {
752 			msg.x0 = -2048;
753 			msg.x1 = 2048;
754 		} else {
755 			msg.y0 = -2048;
756 			msg.y1 = 2048;
757 		}
758 	}
759 	msg.msg = SCROLLBAR_MSG_SCROLL_START;
760 	s->client_callback(s->client_data, &msg);
761 }
762 
763 
764 /*
765  * Exported interface.  Documented in scrollbar.h
766  */
767 scrollbar_mouse_status
scrollbar_mouse_action(struct scrollbar * s,browser_mouse_state mouse,int x,int y)768 scrollbar_mouse_action(struct scrollbar *s,
769 		       browser_mouse_state mouse,
770 		       int x, int y)
771 {
772 	int x0, y0, x1, y1;
773 	int val;
774 	scrollbar_mouse_status status = SCROLLBAR_MOUSE_NONE;
775 	bool h;
776 
777 	/* we want mouse presses and mouse drags that were not started at the
778 	 * scrollbar indication bar to be launching actions on the scroll area
779 	 */
780 	bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) ||
781 		     ((mouse & BROWSER_MOUSE_HOLDING_1) &&
782 		      (mouse & BROWSER_MOUSE_DRAG_ON) &&
783 		      !s->dragging));
784 	bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) ||
785 		     ((mouse & BROWSER_MOUSE_HOLDING_2) &&
786 		      (mouse & BROWSER_MOUSE_DRAG_ON) &&
787 		      !s->dragging));
788 
789 	h = s->horizontal;
790 
791 	x0 = 0;
792 	y0 = 0;
793 	x1 = h ? s->length : SCROLLBAR_WIDTH;
794 	y1 = h ? SCROLLBAR_WIDTH : s->length;
795 
796 	if (!s->dragging && !(x >= x0 && x <= x1 && y >= y0 && y <= y1)) {
797 		/* Not a drag and mouse outside scrollbar widget */
798 		return SCROLLBAR_MOUSE_NONE;
799 	}
800 
801 
802 	if (h) {
803 		val = x;
804 	} else {
805 		val = y;
806 	}
807 
808 	if (s->dragging) {
809 		val -= s->drag_start_coord;
810 		if (s->drag_content) {
811 			val = -val;
812 		}
813 		if (val != 0) {
814 			scrollbar_set(s, s->drag_start_pos + val,
815 				      !(s->drag_content));
816 		}
817 		if (s->pair_drag) {
818 			scrollbar_mouse_action(s->pair, mouse, x, y);
819 			status = SCROLLBAR_MOUSE_BOTH;
820 		} else {
821 			status = h ? SCROLLBAR_MOUSE_HRZ : SCROLLBAR_MOUSE_VRT;
822 		}
823 		return status;
824 	}
825 
826 	if (val < SCROLLBAR_WIDTH) {
827 		/* left/up arrow */
828 
829 		status = h ? SCROLLBAR_MOUSE_LFT : SCROLLBAR_MOUSE_UP;
830 		if (but1) {
831 			scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false);
832 		} else if (but2) {
833 			scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false);
834 		}
835 	} else if (val < SCROLLBAR_WIDTH + s->bar_pos) {
836 		/* well between left/up arrow and bar */
837 
838 		status = h ? SCROLLBAR_MOUSE_PLFT : SCROLLBAR_MOUSE_PUP;
839 
840 		if (but1) {
841 			scrollbar_set(s, s->offset - s->length, false);
842 		} else if (but2) {
843 			scrollbar_set(s, s->offset + s->length, false);
844 		}
845 	} else if (val > s->length - SCROLLBAR_WIDTH) {
846 		/* right/down arrow */
847 
848 		status = h ? SCROLLBAR_MOUSE_RGT : SCROLLBAR_MOUSE_DWN;
849 
850 		if (but1) {
851 			scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false);
852 		} else if (but2) {
853 			scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false);
854 		}
855 	} else if (val > SCROLLBAR_WIDTH + s->bar_pos +	s->bar_len) {
856 		/* well between right/down arrow and bar */
857 
858 		status = h ? SCROLLBAR_MOUSE_PRGT : SCROLLBAR_MOUSE_PDWN;
859 		if (but1) {
860 			scrollbar_set(s, s->offset + s->length, false);
861 		} else if (but2) {
862 			scrollbar_set(s, s->offset - s->length, false);
863 		}
864 	} else {
865 		/* scrollbar position indication bar */
866 
867 		status = h ? SCROLLBAR_MOUSE_HRZ : SCROLLBAR_MOUSE_VRT;
868 	}
869 
870 
871 	if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2) &&
872 	    (val >= SCROLLBAR_WIDTH + s->bar_pos
873 	     && val < SCROLLBAR_WIDTH + s->bar_pos +
874 	     s->bar_len)) {
875 		/* The mouse event is a drag start on the scrollbar position
876 		 * indication bar. */
877 		scrollbar_drag_start_internal(s, x, y, false,
878 					      (mouse & BROWSER_MOUSE_DRAG_2) ? true : false);
879 	}
880 
881 	return status;
882 }
883 
884 
885 /*
886  * Exported interface.  Documented in scrollbar.h
887  */
scrollbar_mouse_status_to_message(scrollbar_mouse_status status)888 const char *scrollbar_mouse_status_to_message(scrollbar_mouse_status status)
889 {
890 	switch ((unsigned int) status) {
891 	case SCROLLBAR_MOUSE_UP:
892 	case SCROLLBAR_MOUSE_UP | SCROLLBAR_MOUSE_USED:
893 		return messages_get("ScrollUp");
894 	case SCROLLBAR_MOUSE_PUP:
895 	case SCROLLBAR_MOUSE_PUP | SCROLLBAR_MOUSE_USED:
896 		return messages_get("ScrollPUp");
897 	case SCROLLBAR_MOUSE_VRT:
898 	case SCROLLBAR_MOUSE_VRT | SCROLLBAR_MOUSE_USED:
899 		return messages_get("ScrollV");
900 	case SCROLLBAR_MOUSE_PDWN:
901 	case SCROLLBAR_MOUSE_PDWN | SCROLLBAR_MOUSE_USED:
902 		return messages_get("ScrollPDown");
903 	case SCROLLBAR_MOUSE_DWN:
904 	case SCROLLBAR_MOUSE_DWN | SCROLLBAR_MOUSE_USED:
905 		return messages_get("ScrollDown");
906 	case SCROLLBAR_MOUSE_LFT:
907 	case SCROLLBAR_MOUSE_LFT | SCROLLBAR_MOUSE_USED:
908 		return messages_get("ScrollLeft");
909 	case SCROLLBAR_MOUSE_PLFT:
910 	case SCROLLBAR_MOUSE_PLFT | SCROLLBAR_MOUSE_USED:
911 		return messages_get("ScrollPLeft");
912 	case SCROLLBAR_MOUSE_HRZ:
913 	case SCROLLBAR_MOUSE_HRZ | SCROLLBAR_MOUSE_USED:
914 		return messages_get("ScrollH");
915 	case SCROLLBAR_MOUSE_PRGT:
916 	case SCROLLBAR_MOUSE_PRGT | SCROLLBAR_MOUSE_USED:
917 		return messages_get("ScrollPRight");
918 	case SCROLLBAR_MOUSE_RGT:
919 	case SCROLLBAR_MOUSE_RGT | SCROLLBAR_MOUSE_USED:
920 		return messages_get("ScrollRight");
921 	default:
922 		break;
923 	}
924 
925 	return NULL;
926 }
927 
928 
929 /*
930  * Exported interface.  Documented in scrollbar.h
931  */
scrollbar_mouse_drag_end(struct scrollbar * s,browser_mouse_state mouse,int x,int y)932 void scrollbar_mouse_drag_end(struct scrollbar *s,
933 			      browser_mouse_state mouse, int x, int y)
934 {
935 	struct scrollbar_msg_data msg;
936 	int val, drag_start_pos;
937 
938 	assert(s->dragging);
939 
940 	drag_start_pos = s->drag_start_pos;
941 	val = (s->horizontal ? x : y) - s->drag_start_coord;
942 
943 	if (s->drag_content) {
944 		val = -val;
945 	}
946 	if (val != 0) {
947 		scrollbar_set(s, drag_start_pos + val, !(s->drag_content));
948 	}
949 
950 	s->dragging = false;
951 	s->drag_content = false;
952 
953 	if (s->pair_drag) {
954 		s->pair_drag = false;
955 
956 		drag_start_pos = s->pair->drag_start_pos;
957 		val = (s->pair->horizontal ? x : y) - s->pair->drag_start_coord;
958 
959 		if (s->pair->drag_content) {
960 			val = -val;
961 		}
962 		if (val != 0) {
963 			scrollbar_set(s->pair, drag_start_pos + val,
964 				      !(s->pair->drag_content));
965 		}
966 
967 		s->pair->dragging = false;
968 		s->pair->drag_content = false;
969 	}
970 
971 	msg.scrollbar = s;
972 	msg.msg = SCROLLBAR_MSG_SCROLL_FINISHED;
973 	s->client_callback(s->client_data, &msg);
974 }
975 
976 
977 /*
978  * Exported interface.  Documented in scrollbar.h
979  */
scrollbar_start_content_drag(struct scrollbar * s,int x,int y)980 void scrollbar_start_content_drag(struct scrollbar *s, int x, int y)
981 {
982 	scrollbar_drag_start_internal(s, x, y, true, true);
983 }
984 
985 
986 /*
987  * Exported interface.  Documented in scrollbar.h
988  */
scrollbar_make_pair(struct scrollbar * horizontal,struct scrollbar * vertical)989 void scrollbar_make_pair(struct scrollbar *horizontal,
990 			 struct scrollbar *vertical)
991 {
992 	assert(horizontal->horizontal &&
993 	       !vertical->horizontal);
994 
995 	horizontal->pair = vertical;
996 	vertical->pair = horizontal;
997 }
998 
999 
1000 /*
1001  * Exported interface.  Documented in scrollbar.h
1002  */
scrollbar_get_data(struct scrollbar * s)1003 void *scrollbar_get_data(struct scrollbar *s)
1004 {
1005 	return s->client_data;
1006 }
1007