Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  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. /** \file
  22.  * Scrollbar widget (implementation).
  23.  */
  24.  
  25. #include <stdbool.h>
  26. #include <stdlib.h>
  27.  
  28. #include "desktop/mouse.h"
  29. #include "desktop/scrollbar.h"
  30. #include "desktop/options.h"
  31. #include "desktop/plotters.h"
  32. #include "desktop/plot_style.h"
  33. #include "utils/log.h"
  34. #include "utils/messages.h"
  35. #include "utils/utils.h"
  36.  
  37.  
  38. struct scrollbar {
  39.         bool horizontal;        /* Horizontal scrollbar if true, else vertical
  40.                                  */
  41.         int length;             /* Length of the scrollbar widget */
  42.  
  43.         int full_size;          /* Length of the full scrollable area */
  44.         int visible_size;       /* Length visible part of the scrollable area */
  45.  
  46.         int offset;             /* Current scroll offset to visible area */
  47.  
  48.         int bar_pos;            /* Position of the scrollbar */
  49.         int bar_len;            /* Length of the scrollbar */
  50.  
  51.         scrollbar_client_callback client_callback;      /* Callback receiving
  52.                                                          * scrollbar events */
  53.         void *client_data;      /* User data passed to the callback */
  54.  
  55.         bool dragging;          /* Flag indicating drag at progess */
  56.         int drag_start_coord;   /* Coordinate value at drag start */
  57.         int drag_start_pos;     /* Scrollbar offset or bar_pos at drag start */
  58.         bool drag_content;      /* Flag indicating that the drag corresponds to
  59.                                  * a dragged content area, rather than a dragged
  60.                                  * scrollbar. */
  61.  
  62.         struct scrollbar *pair; /* Parpendicular scrollbar, or NULL */
  63.         bool pair_drag;         /* Flag indicating that the current drag affects
  64.                                    the perpendicular scrollbar too */
  65. };
  66.  
  67.  
  68.  
  69. /*
  70.  * Exported function.  Documented in scrollbar.h
  71.  */
  72. bool scrollbar_create(bool horizontal, int length, int full_size,
  73.                 int visible_size, void *client_data,
  74.                 scrollbar_client_callback client_callback,
  75.                 struct scrollbar **s)
  76. {
  77.         struct scrollbar *scrollbar;
  78.         int well_length;
  79.  
  80.         scrollbar = malloc(sizeof(struct scrollbar));
  81.         if (scrollbar == NULL) {
  82.                 LOG(("malloc failed"));
  83.                 warn_user("NoMemory", 0);
  84.                 *s = NULL;
  85.                 return false;
  86.         }
  87.  
  88.         scrollbar->horizontal = horizontal;
  89.         scrollbar->length = length;
  90.         scrollbar->full_size = full_size;
  91.         scrollbar->visible_size = visible_size;
  92.         scrollbar->offset = 0;
  93.         scrollbar->bar_pos = 0;
  94.         scrollbar->pair = NULL;
  95.         scrollbar->pair_drag = false;
  96.  
  97.         well_length = length - 2 * SCROLLBAR_WIDTH;
  98.         scrollbar->bar_len = (full_size == 0) ? 0 :
  99.                         ((well_length * visible_size) / full_size);
  100.  
  101.         scrollbar->client_callback = client_callback;
  102.         scrollbar->client_data = client_data;
  103.  
  104.         scrollbar->dragging = false;
  105.         scrollbar->drag_content = false;
  106.  
  107.         *s = scrollbar;
  108.  
  109.         return true;
  110. }
  111.  
  112.  
  113. /*
  114.  * Exported function.  Documented in scrollbar.h
  115.  */
  116. void scrollbar_destroy(struct scrollbar *s)
  117. {
  118.         if (s->pair != NULL)
  119.                 s->pair->pair = NULL;
  120.         free(s);
  121. }
  122.  
  123.  
  124. /**
  125.  * Draw an outline rectangle common to a several scrollbar elements.
  126.  *
  127.  * \param x0    left border of the outline
  128.  * \param y0    top border of the outline
  129.  * \param x1    right border of the outline
  130.  * \param y1    bottom border of the outline
  131.  * \param c     base colour of the outline, the other colours are created by
  132.  *              lightening or darkening this one
  133.  * \param ctx   current redraw context
  134.  * \param inset true for inset outline, false for an outset one
  135.  * \return
  136.  */
  137.  
  138. static inline bool scrollbar_redraw_scrollbar_rectangle(int x0, int y0,
  139.                 int x1, int y1, colour c, bool inset,
  140.                 const struct redraw_context *ctx)
  141. {
  142.         const struct plotter_table *plot = ctx->plot;
  143.  
  144.         static plot_style_t c0 = {
  145.                 .stroke_type = PLOT_OP_TYPE_SOLID,
  146.                 .stroke_width = 1,
  147.         };
  148.  
  149.         static plot_style_t c1 = {
  150.                 .stroke_type = PLOT_OP_TYPE_SOLID,
  151.                 .stroke_width = 1,
  152.         };
  153.  
  154.         static plot_style_t c2 = {
  155.                 .stroke_type = PLOT_OP_TYPE_SOLID,
  156.                 .stroke_width = 1,
  157.         };
  158.  
  159.         if (inset) {
  160.                 c0.stroke_colour = darken_colour(c);
  161.                 c1.stroke_colour = lighten_colour(c);
  162.         } else {
  163.                 c0.stroke_colour = lighten_colour(c);
  164.                 c1.stroke_colour = darken_colour(c);
  165.         }
  166.         c2.stroke_colour = blend_colour(c0.stroke_colour, c1.stroke_colour);
  167.  
  168.         /* Plot the outline */
  169.         if (!plot->line(x0, y0, x1, y0, &c0)) return false;
  170.         if (!plot->line(x1, y0, x1, y1 + 1, &c1)) return false;
  171.         if (!plot->line(x1, y0, x1, y0 + 1, &c2)) return false;
  172.         if (!plot->line(x1, y1, x0, y1, &c1)) return false;
  173.         if (!plot->line(x0, y1, x0, y0, &c0)) return false;
  174.         if (!plot->line(x0, y1, x0, y1 + 1, &c2)) return false;
  175.  
  176.         return true;
  177. }
  178.  
  179.  
  180. /*
  181.  * Exported function.  Documented in scrollbar.h
  182.  */
  183. bool scrollbar_redraw(struct scrollbar *s, int x, int y,
  184.                 const struct rect *clip, float scale,
  185.                 const struct redraw_context *ctx)
  186. {
  187.         const struct plotter_table *plot = ctx->plot;
  188.         int w = SCROLLBAR_WIDTH;
  189.         int bar_pos, bar_c0, bar_c1;
  190.         int v[6]; /* array of triangle vertices */
  191.         int x0, y0, x1, y1;
  192.  
  193.         colour bg_fill_colour = gui_system_colour_char("Scrollbar");
  194.         colour fg_fill_colour = gui_system_colour_char("ButtonFace");
  195.         colour arrow_fill_colour = gui_system_colour_char("ButtonText");
  196.  
  197.         plot_style_t bg_fill_style = {
  198.                 .fill_type = PLOT_OP_TYPE_SOLID,
  199.                 .fill_colour = bg_fill_colour
  200.         };
  201.         plot_style_t fg_fill_style = {
  202.                 .fill_type = PLOT_OP_TYPE_SOLID,
  203.                 .fill_colour = fg_fill_colour
  204.         };
  205.         plot_style_t arrow_fill_style = {
  206.                 .fill_type = PLOT_OP_TYPE_SOLID,
  207.                 .fill_colour = arrow_fill_colour
  208.         };
  209.  
  210.         x0 = x;
  211.         y0 = y;
  212.         x1 = x + (s->horizontal ? s->length : SCROLLBAR_WIDTH) - 1;
  213.         y1 = y + (s->horizontal ? SCROLLBAR_WIDTH : s->length) - 1;
  214.         bar_pos = s->bar_pos;
  215.         bar_c1 = (s->horizontal ? x0 : y0) + SCROLLBAR_WIDTH +
  216.                         s->bar_pos + s->bar_len - 1;
  217.  
  218.         if (scale != 1.0) {
  219.                 w *= scale;
  220.                 x0 *= scale;
  221.                 y0 *= scale;
  222.                 x1 *= scale;
  223.                 y1 *= scale;
  224.                 bar_pos *= scale;
  225.                 bar_c1 *= scale;
  226.         }
  227.  
  228.         bar_c0 = (s->horizontal ? x0 : y0) + w + bar_pos;
  229.  
  230.         if (x1 < clip->x0 || y1 < clip->y0 || clip->x1 < x0 || clip->y1 < y0)
  231.                 /* scrollbar is outside the clipping rectangle, nothing to
  232.                  * render */
  233.                 return true;
  234.  
  235.        
  236.         if (s->horizontal) {
  237.                 /* scrollbar is horizontal */
  238.                
  239.                 /* scrollbar outline */
  240.                 if (!scrollbar_redraw_scrollbar_rectangle(x0, y0, x1, y1,
  241.                                 bg_fill_colour, true, ctx))
  242.                         return false;
  243.                 /* left arrow icon border */
  244.                 if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1,
  245.                                 y0 + 1,
  246.                                 x0 + w - 2,
  247.                                 y1 - 1,
  248.                                 fg_fill_colour, false, ctx))
  249.                         return false;
  250.                 /* left arrow icon background */
  251.                 if (!plot->rectangle(x0 + 2,
  252.                                 y0 + 2,
  253.                                 x0 + w - 2,
  254.                                 y1 - 1,
  255.                                 &fg_fill_style))
  256.                         return false;
  257.                 /* left arrow */
  258.                 v[0] = x0 + w / 4;
  259.                 v[1] = y0 + w / 2;
  260.                 v[2] = x0 + w * 3 / 4;
  261.                 v[3] = y0 + w / 4;
  262.                 v[4] = x0 + w * 3 / 4;
  263.                 v[5] = y0 + w * 3 / 4;
  264.                 if (!plot->polygon(v, 3, &arrow_fill_style))
  265.                         return false;
  266.                 /* scrollbar well background */
  267.                 if (!plot->rectangle(x0 + w - 1,
  268.                                 y0 + 1,
  269.                                 x1 - w + 2,
  270.                                 y1,
  271.                                 &bg_fill_style))
  272.                         return false;
  273.                 /* scrollbar position indicator bar */
  274.                 if (!scrollbar_redraw_scrollbar_rectangle(bar_c0,
  275.                                 y0 + 1,
  276.                                 bar_c1,
  277.                                 y1 - 1,
  278.                                 fg_fill_colour, false, ctx))
  279.                         return false;
  280.                 if (!plot->rectangle(bar_c0 + 1,
  281.                                 y0 + 2,
  282.                                 bar_c1,
  283.                                 y1 - 1,
  284.                                 &fg_fill_style))
  285.                         return false;
  286.                 /* right arrow icon border */
  287.                 if (!scrollbar_redraw_scrollbar_rectangle(x1 - w + 2,
  288.                                 y0 + 1,
  289.                                 x1 - 1,
  290.                                 y1 - 1,
  291.                                 fg_fill_colour, false, ctx))
  292.                         return false;
  293.                 /* right arrow icon background */
  294.                 if (!plot->rectangle(x1 - w + 3,
  295.                                 y0 + 2,
  296.                                 x1 - 1,
  297.                                 y1 - 1,
  298.                                 &fg_fill_style))
  299.                         return false;
  300.                 /* right arrow */
  301.                 v[0] = x1 - w / 4 + 1;
  302.                 v[1] = y0 + w / 2;
  303.                 v[2] = x1 - w * 3 / 4 + 1;
  304.                 v[3] = y0 + w / 4;
  305.                 v[4] = x1 - w * 3 / 4 + 1;
  306.                 v[5] = y0 + w * 3 / 4;
  307.                 if (!plot->polygon(v, 3, &arrow_fill_style))
  308.                         return false;
  309.         } else {
  310.                 /* scrollbar is vertical */
  311.                
  312.                 /* outline */
  313.                 if (!scrollbar_redraw_scrollbar_rectangle(x0, y0, x1, y1,
  314.                                 bg_fill_colour, true, ctx))
  315.                         return false;
  316.                 /* top arrow background */
  317.                 if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1,
  318.                                 y0 + 1,
  319.                                 x1 - 1,
  320.                                 y0 + w - 2,
  321.                                 fg_fill_colour, false, ctx))
  322.                         return false;
  323.                 if (!plot->rectangle(x0 + 2,
  324.                                 y0 + 2,
  325.                                 x1 - 1,
  326.                                 y0 + w - 2,
  327.                                 &fg_fill_style))
  328.                         return false;
  329.                 /* up arrow */
  330.                 v[0] = x0 + w / 2;
  331.                 v[1] = y0 + w / 4;
  332.                 v[2] = x0 + w / 4;
  333.                 v[3] = y0 + w * 3 / 4;
  334.                 v[4] = x0 + w * 3 / 4;
  335.                 v[5] = y0 + w * 3 / 4;
  336.                 if (!plot->polygon(v, 3, &arrow_fill_style))
  337.                         return false;
  338.                 /* scrollbar well background */
  339.                 if (!plot->rectangle(x0 + 1,
  340.                                 y0 + w - 1,
  341.                                 x1,
  342.                                 y1 - w + 2,
  343.                                 &bg_fill_style))
  344.                         return false;
  345.                 /* scrollbar position indicator bar */
  346.                 if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1,
  347.                                 bar_c0,
  348.                                 x1 - 1,
  349.                                 bar_c1,
  350.                                 fg_fill_colour, false, ctx))
  351.                         return false;
  352.                 if (!plot->rectangle(x0 + 2,
  353.                                 bar_c0 + 1,
  354.                                 x1 - 1,
  355.                                 bar_c1,
  356.                                 &fg_fill_style))
  357.                         return false;
  358.                 /* bottom arrow background */
  359.                 if (!scrollbar_redraw_scrollbar_rectangle(x0 + 1,
  360.                                 y1 - w + 2,
  361.                                 x1 - 1,
  362.                                 y1 - 1,
  363.                                 fg_fill_colour, false, ctx))
  364.                         return false;
  365.                 if (!plot->rectangle(x0 + 2,
  366.                                 y1 - w + 3,
  367.                                 x1 - 1,
  368.                                 y1 - 1,
  369.                                 &fg_fill_style))
  370.                         return false;
  371.                 /* down arrow */
  372.                 v[0] = x0 + w / 2;
  373.                 v[1] = y1 - w / 4 + 1;
  374.                 v[2] = x0 + w / 4;
  375.                 v[3] = y1 - w * 3 / 4 + 1;
  376.                 v[4] = x0 + w * 3 / 4;
  377.                 v[5] = y1 - w * 3 / 4 + 1;
  378.                 if (!plot->polygon(v, 3, &arrow_fill_style))
  379.                         return false;
  380.         }
  381.  
  382.         return true;
  383. }
  384.  
  385.  
  386. /*
  387.  * Exported function.  Documented in scrollbar.h
  388.  */
  389. void scrollbar_set(struct scrollbar *s, int value, bool bar_pos)
  390. {
  391.         int well_length;
  392.         int old_offset = s->offset;
  393.         struct scrollbar_msg_data msg;
  394.  
  395.         if (value < 0)
  396.                 value = 0;
  397.  
  398.         if (s->full_size == s->visible_size)
  399.                 return;
  400.  
  401.         well_length = s->length - 2 * SCROLLBAR_WIDTH;
  402.         if (bar_pos) {
  403.                 if (value > well_length - s->bar_len)
  404.                         s->bar_pos = well_length - s->bar_len;
  405.                 else
  406.                         s->bar_pos = value;
  407.  
  408.                 s->offset = ((well_length - s->bar_len) < 1) ? 0 :
  409.                                 (((s->full_size - s->visible_size) *
  410.                                 s->bar_pos) / (well_length - s->bar_len));
  411.  
  412.         } else {
  413.                 if (value > s->full_size - s->visible_size)
  414.                         s->offset = s->full_size - s->visible_size;
  415.                 else
  416.                         s->offset = value;
  417.  
  418.                 s->bar_pos = (s->full_size < 1) ? 0 :
  419.                                 ((well_length * s->offset) / s->full_size);
  420.         }
  421.  
  422.         if (s->offset == old_offset)
  423.                 /* Nothing happened */
  424.                 return;
  425.  
  426.         msg.scrollbar = s;
  427.         msg.msg = SCROLLBAR_MSG_MOVED;
  428.         msg.scroll_offset = s->offset;
  429.         s->client_callback(s->client_data, &msg);
  430. }
  431.  
  432.  
  433. /*
  434.  * Exported function.  Documented in scrollbar.h
  435.  */
  436. bool scrollbar_scroll(struct scrollbar *s, int change)
  437. {
  438.         int well_length;
  439.         int old_offset = s->offset;
  440.         struct scrollbar_msg_data msg;
  441.  
  442.         if (change == 0 || s->full_size <= s->visible_size)
  443.                 /* zero scroll step, or unscrollable */
  444.                 return false;
  445.  
  446.         /* Convert named change values to appropriate pixel offset value */
  447.         switch (change) {
  448.         case SCROLL_TOP:
  449.                 change = -s->full_size;
  450.                 break;
  451.  
  452.         case SCROLL_PAGE_UP:
  453.                 change = -s->visible_size;
  454.                 break;
  455.  
  456.         case SCROLL_PAGE_DOWN:
  457.                 change = s->visible_size;
  458.                 break;
  459.  
  460.         case SCROLL_BOTTOM:
  461.                 change = s->full_size;
  462.                 break;
  463.  
  464.         default:
  465.                 /* Change value is already a pixel offset */
  466.                 break;
  467.         }
  468.  
  469.         /* Get new offset */
  470.         if (s->offset + change > s->full_size - s->visible_size)
  471.                 s->offset = s->full_size - s->visible_size;
  472.         else if (s->offset + change < 0)
  473.                 s->offset = 0;
  474.         else
  475.                 s->offset += change;
  476.  
  477.         if (s->offset == old_offset)
  478.                 /* Nothing happened */
  479.                 return false;
  480.  
  481.         /* Update scrollbar */
  482.         well_length = s->length - 2 * SCROLLBAR_WIDTH;
  483.         s->bar_pos = (s->full_size < 1) ? 0 :
  484.                         ((well_length * s->offset) / s->full_size);
  485.  
  486.         msg.scrollbar = s;
  487.         msg.msg = SCROLLBAR_MSG_MOVED;
  488.         msg.scroll_offset = s->offset;
  489.         s->client_callback(s->client_data, &msg);
  490.  
  491.         return true;
  492. }
  493.  
  494.  
  495. /*
  496.  * Exported function.  Documented in scrollbar.h
  497.  */
  498. int scrollbar_get_offset(struct scrollbar *s)
  499. {
  500.         if (s == NULL)
  501.                 return 0;
  502.         return s->offset;
  503. }
  504.  
  505.  
  506. /*
  507.  * Exported function.  Documented in scrollbar.h
  508.  */
  509. void scrollbar_set_extents(struct scrollbar *s, int length,
  510.                 int visible_size, int full_size)
  511. {
  512.         int well_length;
  513.         int cur_excess = s->full_size - s->visible_size;
  514.  
  515.         if (length != -1)
  516.                 s->length = length;
  517.         if (visible_size != -1)
  518.                 s->visible_size = visible_size;
  519.         if (full_size != -1)
  520.                 s->full_size = full_size;
  521.  
  522.         if (s->full_size < s->visible_size)
  523.                 s->full_size = s->visible_size;
  524.  
  525.         /* Update scroll offset (scaled in proportion with change in excess) */
  526.         s->offset = (cur_excess < 1) ? 0 :
  527.                         ((s->full_size - s->visible_size) * s->offset /
  528.                         cur_excess);
  529.  
  530.         well_length = s->length - 2 * SCROLLBAR_WIDTH;
  531.  
  532.         if (s->full_size < 1) {
  533.                 s->bar_len = well_length;
  534.                 s->bar_pos = 0;
  535.         } else {
  536.                 s->bar_len = (well_length * s->visible_size) / s->full_size;
  537.                 s->bar_pos = (well_length * s->offset) / s->full_size;
  538.         }
  539. }
  540.  
  541.  
  542. /*
  543.  * Exported function.  Documented in scrollbar.h
  544.  */
  545. bool scrollbar_is_horizontal(struct scrollbar *s)
  546. {
  547.         return s->horizontal;
  548. }
  549.  
  550.  
  551. /**
  552.  * Internal procedure used for starting a drag scroll for a scrollbar.
  553.  *
  554.  * \param s             the scrollbar to start the drag for
  555.  * \param x             the X coordinate of the drag start
  556.  * \param y             the Y coordinate of the drag start
  557.  * \param content_drag  whether this should be a reverse drag (used when the
  558.  *                      user drags the content area, rather than the scrollbar)
  559.  * \param pair          whether the drag is a '2D' scroll
  560.  */
  561.  
  562. static void scrollbar_drag_start_internal(struct scrollbar *s, int x, int y,
  563.                 bool content_drag, bool pair)
  564. {
  565.         struct scrollbar_msg_data msg;
  566.  
  567.         s->drag_start_coord = s->horizontal ? x : y;
  568.         s->drag_start_pos = (content_drag) ? s->offset : s->bar_pos;
  569.  
  570.         s->dragging = true;
  571.         s->drag_content = content_drag;
  572.  
  573.         msg.scrollbar = s;
  574.  
  575.         /* \todo - some proper numbers please! */
  576.         if (s->horizontal) {
  577.                 msg.x0 = -2048;
  578.                 msg.x1 = 2048;
  579.                 msg.y0 = 0;
  580.                 msg.y1 = 0;
  581.         } else {
  582.                 msg.x0 = 0;
  583.                 msg.x1 = 0;
  584.                 msg.y0 = -2048;
  585.                 msg.y1 = 2048;
  586.         }
  587.  
  588.         if (pair && s->pair != NULL) {
  589.                 s->pair_drag = true;
  590.  
  591.                 s->pair->drag_start_coord =
  592.                                 s->pair->horizontal ? x : y;
  593.  
  594.                 s->pair->drag_start_pos = (content_drag) ? s->pair->offset :
  595.                                 s->pair->bar_pos;
  596.  
  597.                 s->pair->dragging = true;
  598.                 s->pair->drag_content = content_drag;
  599.  
  600.                 if (s->pair->horizontal) {
  601.                         msg.x0 = -2048;
  602.                         msg.x1 = 2048;
  603.                 } else {
  604.                         msg.y0 = -2048;
  605.                         msg.y1 = 2048;
  606.                 }
  607.         }
  608.         msg.msg = SCROLLBAR_MSG_SCROLL_START;
  609.         s->client_callback(s->client_data, &msg);
  610. }
  611.  
  612.  
  613. /*
  614.  * Exported function.  Documented in scrollbar.h
  615.  */
  616. const char *scrollbar_mouse_action(struct scrollbar *s,
  617.                 browser_mouse_state mouse, int x, int y)
  618. {
  619.         int x0, y0, x1, y1;
  620.         int val;
  621.         const char *status;
  622.         bool h;
  623.  
  624.         /* we want mouse presses and mouse drags that were not started at the
  625.          * scrollbar indication bar to be launching actions on the scroll area
  626.          */
  627.         bool but1 = ((mouse & BROWSER_MOUSE_PRESS_1) ||
  628.                         ((mouse & BROWSER_MOUSE_HOLDING_1) &&
  629.                         (mouse & BROWSER_MOUSE_DRAG_ON) &&
  630.                         !s->dragging));
  631.         bool but2 = ((mouse & BROWSER_MOUSE_PRESS_2) ||
  632.                         ((mouse & BROWSER_MOUSE_HOLDING_2) &&
  633.                         (mouse & BROWSER_MOUSE_DRAG_ON) &&
  634.                         !s->dragging));
  635.  
  636.         h = s->horizontal;
  637.  
  638.         x0 = 0;
  639.         y0 = 0;
  640.         x1 = h ? s->length : SCROLLBAR_WIDTH;
  641.         y1 = h ? SCROLLBAR_WIDTH : s->length;
  642.  
  643.         if (!s->dragging && !(x >= x0 && x <= x1 && y >= y0 && y <= y1)) {
  644.                 /* Not a drag and mouse outside scrollbar widget */
  645.                 return NULL;
  646.         }
  647.  
  648.  
  649.         if (h)
  650.                 val = x;
  651.         else
  652.                 val = y;
  653.  
  654.         if (s->dragging) {
  655.                 val -= s->drag_start_coord;
  656.                 if (s->drag_content)
  657.                         val = -val;
  658.                 if (val != 0)
  659.                         scrollbar_set(s, s->drag_start_pos + val,
  660.                                         !(s->drag_content));
  661.                 if (s->pair_drag) {
  662.                         scrollbar_mouse_action(s->pair, mouse, x, y);
  663.                         status = messages_get("ScrollBoth");
  664.                 } else
  665.                         status = messages_get(h ? "ScrollH" : "ScrollV");
  666.  
  667.                 return status;
  668.         }
  669.  
  670.         if (val < SCROLLBAR_WIDTH) {
  671.                 /* left/up arrow */
  672.                
  673.                 status = messages_get(h ? "ScrollLeft" : "ScrollUp");
  674.                 if (but1)
  675.                         scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false);
  676.                 else if (but2)
  677.                         scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false);
  678.  
  679.         } else if (val < SCROLLBAR_WIDTH + s->bar_pos) {
  680.                 /* well between left/up arrow and bar */
  681.  
  682.                 status = messages_get(h ? "ScrollPLeft" : "ScrollPUp");
  683.  
  684.                 if (but1)
  685.                         scrollbar_set(s, s->offset - s->length, false);
  686.                 else if (but2)
  687.                         scrollbar_set(s, s->offset + s->length, false);
  688.  
  689.         } else if (val > s->length - SCROLLBAR_WIDTH) {
  690.                 /* right/down arrow */
  691.  
  692.                 status = messages_get(h ? "ScrollRight" : "ScrollDown");
  693.  
  694.                 if (but1)
  695.                         scrollbar_set(s, s->offset + SCROLLBAR_WIDTH, false);
  696.                 else if (but2)
  697.                         scrollbar_set(s, s->offset - SCROLLBAR_WIDTH, false);
  698.  
  699.         } else if (val > SCROLLBAR_WIDTH + s->bar_pos +
  700.                         s->bar_len) {
  701.                 /* well between right/down arrow and bar */
  702.  
  703.                 status = messages_get(h ? "ScrollPRight" : "ScrollPDown");
  704.                 if (but1)
  705.                         scrollbar_set(s, s->offset + s->length, false);
  706.                 else if (but2)
  707.                         scrollbar_set(s, s->offset - s->length, false);
  708.         }
  709.         else {
  710.                 /* scrollbar position indication bar */
  711.                
  712.                 status = messages_get(h ? "ScrollH" : "ScrollV");
  713.         }
  714.  
  715.        
  716.         if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2) &&
  717.                         (val >= SCROLLBAR_WIDTH + s->bar_pos
  718.                         && val < SCROLLBAR_WIDTH + s->bar_pos +
  719.                                         s->bar_len))
  720.                 /* The mouse event is a drag start on the scrollbar position
  721.                  * indication bar. */
  722.                 scrollbar_drag_start_internal(s, x, y, false,
  723.                                 (mouse & BROWSER_MOUSE_DRAG_2) ? true : false);
  724.  
  725.         return status;
  726. }
  727.  
  728.  
  729. /*
  730.  * Exported function.  Documented in scrollbar.h
  731.  */
  732. void scrollbar_mouse_drag_end(struct scrollbar *s,
  733.                 browser_mouse_state mouse, int x, int y)
  734. {
  735.         struct scrollbar_msg_data msg;
  736.         int val, drag_start_pos;
  737.  
  738.         assert(s->dragging);
  739.  
  740.         drag_start_pos = s->drag_start_pos;
  741.         val = (s->horizontal ? x : y) - s->drag_start_coord;
  742.  
  743.         if (s->drag_content)
  744.                 val = -val;
  745.         if (val != 0)
  746.                 scrollbar_set(s, drag_start_pos + val, !(s->drag_content));
  747.  
  748.         s->dragging = false;
  749.         s->drag_content = false;
  750.  
  751.         if (s->pair_drag) {
  752.                 s->pair_drag = false;
  753.  
  754.                 drag_start_pos = s->pair->drag_start_pos;
  755.                 val = (s->pair->horizontal ? x : y) - s->pair->drag_start_coord;
  756.  
  757.                 if (s->pair->drag_content)
  758.                         val = -val;
  759.                 if (val != 0)
  760.                         scrollbar_set(s->pair, drag_start_pos + val,
  761.                                         !(s->pair->drag_content));
  762.  
  763.                 s->pair->dragging = false;
  764.                 s->pair->drag_content = false;
  765.         }
  766.  
  767.         msg.scrollbar = s;
  768.         msg.msg = SCROLLBAR_MSG_SCROLL_FINISHED;
  769.         s->client_callback(s->client_data, &msg);
  770. }
  771.  
  772.  
  773. /*
  774.  * Exported function.  Documented in scrollbar.h
  775.  */
  776. void scrollbar_start_content_drag(struct scrollbar *s, int x, int y)
  777. {
  778.         scrollbar_drag_start_internal(s, x, y, true, true);
  779. }
  780.  
  781.  
  782. /*
  783.  * Exported function.  Documented in scrollbar.h
  784.  */
  785. void scrollbar_make_pair(struct scrollbar *horizontal,
  786.                 struct scrollbar *vertical)
  787. {
  788.         assert(horizontal->horizontal &&
  789.                         !vertical->horizontal);
  790.  
  791.         horizontal->pair = vertical;
  792.         vertical->pair = horizontal;
  793. }
  794.  
  795.  
  796. /*
  797.  * Exported function.  Documented in scrollbar.h
  798.  */
  799. void *scrollbar_get_data(struct scrollbar *s)
  800. {
  801.         return s->client_data;
  802. }
  803.  
  804.