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