Subversion Repositories Kolibri OS

Rev

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

  1. /*
  2.  * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
  3.  * Copyright 2008 Michael Drake <tlsa@netsurf-browser.org>
  4.  *
  5.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  6.  *
  7.  * NetSurf is free software; you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License as published by
  9.  * the Free Software Foundation; version 2 of the License.
  10.  *
  11.  * NetSurf is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  */
  19.  
  20. /** \file
  21.   * Text selection within browser windows (implementation).
  22.   */
  23.  
  24. #include <assert.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <stdbool.h>
  28. #include <string.h>
  29. #include <dom/dom.h>
  30.  
  31. #include "desktop/browser_private.h"
  32. #include "desktop/gui.h"
  33. #include "desktop/mouse.h"
  34. #include "desktop/plotters.h"
  35. #include "desktop/save_text.h"
  36. #include "desktop/selection.h"
  37. #include "render/box.h"
  38. #include "render/font.h"
  39. #include "render/form.h"
  40. #include "render/html_internal.h"
  41. #include "render/textplain.h"
  42. #include "utils/log.h"
  43. #include "utils/utf8.h"
  44. #include "utils/utils.h"
  45.  
  46.  
  47. /**
  48.  * Text selection works by labelling each node in the box tree with its
  49.  * start index in the textual representation of the tree's content.
  50.  *
  51.  * Text input fields and text areas have their own number spaces so that
  52.  * they can be relabelled more efficiently when editing (rather than relabel
  53.  * the entire box tree) and so that selections are either wholly within
  54.  * or wholly without the textarea/input box.
  55.  */
  56.  
  57. #define IS_INPUT(box) ((box) && (box)->gadget && \
  58.         ((box)->gadget->type == GADGET_TEXTAREA || \
  59.                 (box)->gadget->type == GADGET_TEXTBOX || \
  60.                 (box)->gadget->type == GADGET_PASSWORD))
  61.  
  62. /** check whether the given text box is in the same number space as the
  63.     current selection; number spaces are identified by their uppermost nybble */
  64.  
  65. #define NUMBER_SPACE(x) ((x) & 0xF0000000U)
  66. #define SAME_SPACE(s, offset) (NUMBER_SPACE((s)->max_idx) == NUMBER_SPACE(offset))
  67.  
  68. #define SPACE_LEN(b) ((b->space == 0) ? 0 : 1)
  69.  
  70.  
  71. struct rdw_info {
  72.         bool inited;
  73.         struct rect r;
  74. };
  75.  
  76. struct selection_string {
  77.         char *buffer;
  78.         size_t buffer_len;
  79.         size_t length;
  80.  
  81.         int n_styles;
  82.         nsclipboard_styles *styles;
  83. };
  84.  
  85.  
  86. typedef bool (*seln_traverse_handler)(const char *text, size_t length,
  87.                 struct box *box, void *handle, const char *whitespace_text,
  88.                 size_t whitespace_length);
  89.  
  90.  
  91. static bool redraw_handler(const char *text, size_t length, struct box *box,
  92.                 void *handle, const char *whitespace_text,
  93.                 size_t whitespace_length);
  94. static void selection_redraw(struct selection *s, unsigned start_idx,
  95.                 unsigned end_idx);
  96. static bool save_handler(const char *text, size_t length, struct box *box,
  97.                 void *handle, const char *whitespace_text,
  98.                 size_t whitespace_length);
  99. static bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
  100.                 unsigned *start_offset, unsigned *end_offset);
  101. static bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
  102.                 unsigned int num_space, seln_traverse_handler handler,
  103.                 void *handle, save_text_whitespace *before, bool *first,
  104.                 bool do_marker);
  105. static struct box *get_box(struct box *b, unsigned offset, size_t *pidx);
  106.  
  107.  
  108. /**
  109.  * Get the browser window containing the content a selection object belongs to.
  110.  *
  111.  * \param  s    selection object
  112.  * \return the browser window
  113.  */
  114. static struct browser_window * selection_get_browser_window(struct selection *s)
  115. {
  116.         if (s->is_html)
  117.                 return html_get_browser_window(s->c);
  118.         else
  119.                 return textplain_get_browser_window(s->c);
  120. }
  121.  
  122.  
  123. /**
  124.  * Creates a new selection object associated with a browser window.
  125.  *
  126.  * \return new selection context
  127.  */
  128.  
  129. struct selection *selection_create(struct content *c, bool is_html)
  130. {
  131.         struct selection *s = calloc(1, sizeof(struct selection));
  132.         if (s) {
  133.                 selection_prepare(s, c, is_html);
  134.         }
  135.  
  136.         return s;
  137. }
  138.  
  139. /**
  140.  * Prepare a newly created selection object for use.
  141.  *
  142.  * \param  s            selection object
  143.  * \param  c            content
  144.  * \param  is_html      true if content is html false if content is textplain
  145.  */
  146.  
  147. void selection_prepare(struct selection *s, struct content *c, bool is_html)
  148. {
  149.         if (s) {
  150.                 s->c = c;
  151.                 s->is_html = is_html;
  152.                 s->root = NULL;
  153.                 s->drag_state = DRAG_NONE;
  154.                 s->max_idx = 0;
  155.                 selection_clear(s, false);
  156.         }
  157. }
  158.  
  159.  
  160. /**
  161.  * Destroys a selection object, without updating the
  162.  * owning window (caller should call selection_clear()
  163.  * first if update is desired)
  164.  *
  165.  * \param  s       selection object
  166.  */
  167.  
  168. void selection_destroy(struct selection *s)
  169. {
  170.         if (s != NULL)
  171.                 free(s);
  172. }
  173.  
  174.  
  175. /**
  176.  * Initialise the selection object to use the given box subtree as its root,
  177.  * ie. selections are confined to that subtree, whilst maintaining the current
  178.  * selection whenever possible because, for example, it's just the page being
  179.  * resized causing the layout to change.
  180.  *
  181.  * \param  s     selection object
  182.  * \param  root  the box (page/textarea) to be used as the root node for this selection
  183.  */
  184.  
  185. void selection_reinit(struct selection *s, struct box *root)
  186. {
  187.         unsigned root_idx;
  188.  
  189.         assert(s);
  190.  
  191.         if (IS_INPUT(root)) {
  192.                 static int next_idx = 0;
  193.                 if (!++next_idx) next_idx = 1;
  194.                 root_idx = next_idx << 28;
  195.         }
  196.         else
  197.                 root_idx = 0;
  198.  
  199. //      if (s->root == root) {
  200. //              /* keep the same number space as before, because we want
  201. //                 to keep the selection too */
  202. //              root_idx = (s->max_idx & 0xF0000000U);
  203. //      }
  204. //      else {
  205. //              static int next_idx = 0;
  206. //              root_idx = (next_idx++) << 28;
  207. //      }
  208.  
  209.         s->root = root;
  210.         if (root) {
  211.                 s->max_idx = selection_label_subtree(root, root_idx);
  212.         } else {
  213.                 if (s->is_html == false)
  214.                         s->max_idx = textplain_size(s->c);
  215.                 else
  216.                         s->max_idx = 0;
  217.         }
  218.  
  219.         if (s->defined) {
  220.                 if (s->end_idx > s->max_idx) s->end_idx = s->max_idx;
  221.                 if (s->start_idx > s->max_idx) s->start_idx = s->max_idx;
  222.                 s->defined = (s->end_idx > s->start_idx);
  223.         }
  224. }
  225.  
  226.  
  227. /**
  228.  * Initialise the selection object to use the given box subtree as its root,
  229.  * ie. selections are confined to that subtree.
  230.  *
  231.  * \param  s     selection object
  232.  * \param  root  the box (page/textarea) to be used as the root node for this selection
  233.  */
  234.  
  235. void selection_init(struct selection *s, struct box *root)
  236. {
  237.         if (s->defined)
  238.                 selection_clear(s, true);
  239.  
  240.         s->defined = false;
  241.         s->start_idx = 0;
  242.         s->end_idx = 0;
  243.         s->drag_state = DRAG_NONE;
  244.  
  245.         selection_reinit(s, root);
  246. }
  247.  
  248.  
  249. /**
  250.  * Indicate whether the selected text is read only, ie. cannot be modified.
  251.  *
  252.  * \param  s   selection object
  253.  * \return true iff the selection is read only
  254.  */
  255.  
  256. bool selection_read_only(struct selection *s)
  257. {
  258.         return !s->root || !NUMBER_SPACE(s->root->byte_offset);
  259.  
  260. }
  261.  
  262.  
  263. /**
  264.  * Label each text box in the given box subtree with its position
  265.  * in a textual representation of the content.
  266.  *
  267.  * \param  s     selection object
  268.  * \param  node  box at root of subtree
  269.  * \param  idx   current position within textual representation
  270.  * \return updated position
  271.  */
  272.  
  273. unsigned selection_label_subtree(struct box *box, unsigned idx)
  274. {
  275.         struct box *child = box->children;
  276.  
  277.         box->byte_offset = idx;
  278.  
  279.         if (box->text)
  280.                 idx += box->length + SPACE_LEN(box);
  281.  
  282.         while (child) {
  283.                 if (!IS_INPUT(child)) {
  284.                         if (child->list_marker)
  285.                                 idx = selection_label_subtree(
  286.                                                 child->list_marker, idx);
  287.  
  288.                         idx = selection_label_subtree(child, idx);
  289.                 }
  290.                 child = child->next;
  291.         }
  292.  
  293.         return idx;
  294. }
  295.  
  296.  
  297. /**
  298.  * Handles mouse clicks (including drag starts) in or near a selection
  299.  *
  300.  * \param  s      selection object
  301.  * \param  mouse  state of mouse buttons and modifier keys
  302.  * \param  idx    byte offset within textual representation
  303.  *
  304.  * \return true iff the click has been handled by the selection code
  305.  */
  306.  
  307. bool selection_click(struct selection *s, browser_mouse_state mouse,
  308.                 unsigned idx)
  309. {
  310.         browser_mouse_state modkeys =
  311.                         (mouse & (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2));
  312.         int pos = -1;  /* 0 = inside selection, 1 = after it */
  313.         struct browser_window *top = selection_get_browser_window(s);
  314.  
  315.         if (top == NULL)
  316.                 return false; /* not our problem */
  317.  
  318.         top = browser_window_get_root(top);
  319.  
  320.         if (!SAME_SPACE(s, idx))
  321.                 return false;   /* not our problem */
  322.  
  323.         if (selection_defined(s)) {
  324.                 if (idx > s->start_idx) {
  325.                         if (idx <= s->end_idx)
  326.                                 pos = 0;
  327.                         else
  328.                                 pos = 1;
  329.                 }
  330.         }
  331.  
  332.         if (!pos &&
  333.                 ((mouse & BROWSER_MOUSE_DRAG_1) ||
  334.                  (modkeys && (mouse & BROWSER_MOUSE_DRAG_2)))) {
  335.                 /* drag-saving selection */
  336.  
  337.                 gui_drag_save_selection(s, top->window);
  338.         }
  339.         else if (!modkeys) {
  340.                 if (pos && (mouse & BROWSER_MOUSE_PRESS_1)) {
  341.                 /* Clear the selection if mouse is pressed outside the selection,
  342.                  * Otherwise clear on release (to allow for drags) */
  343.                         browser_window_set_selection(top, s);
  344.  
  345.                         selection_clear(s, true);
  346.                 } else if (mouse & BROWSER_MOUSE_DRAG_1) {
  347.                         /* start new selection drag */
  348.                         browser_window_set_selection(top, s);
  349.  
  350.                         selection_clear(s, true);
  351.                        
  352.                         selection_set_start(s, idx);
  353.                         selection_set_end(s, idx);
  354.  
  355.                         s->drag_state = DRAG_END;
  356.  
  357.                         gui_start_selection(top->window);
  358.                 }
  359.                 else if (mouse & BROWSER_MOUSE_DRAG_2) {
  360.  
  361.                         /* adjust selection, but only if there is one */
  362.                         if (!selection_defined(s))
  363.                                 return false;   /* ignore Adjust drags */
  364.  
  365.                         browser_window_set_selection(top, s);
  366.  
  367.                         if (pos >= 0) {
  368.                                 selection_set_end(s, idx);
  369.  
  370.                                 s->drag_state = DRAG_END;
  371.                         }
  372.                         else {
  373.                                 selection_set_start(s, idx);
  374.  
  375.                                 s->drag_state = DRAG_START;
  376.                         }
  377.  
  378.                         gui_start_selection(top->window);
  379.                 }
  380.                 /* Selection should be cleared when button is released but in
  381.                  * the RO interface click is the same as press */
  382. //              else if (!pos && (mouse & BROWSER_MOUSE_CLICK_1)) {
  383. //                      /* clear selection */
  384. //                      selection_clear(s, true);
  385. //                      s->drag_state = DRAG_NONE;
  386. //              }
  387.                 else if (mouse & BROWSER_MOUSE_CLICK_2) {
  388.  
  389.                         /* ignore Adjust clicks when there's no selection */
  390.                         if (!selection_defined(s))
  391.                                 return false;
  392.  
  393.                         if (pos >= 0)
  394.                                 selection_set_end(s, idx);
  395.                         else
  396.                                 selection_set_start(s, idx);
  397.                         s->drag_state = DRAG_NONE;
  398.                 }
  399.                 else
  400.                         return false;
  401.         }
  402.         else {
  403.                 /* not our problem */
  404.                 return false;
  405.         }
  406.  
  407.         /* this mouse click is selection-related */
  408.         return true;
  409. }
  410.  
  411.  
  412. /**
  413.  * Handles movements related to the selection, eg. dragging of start and
  414.  * end points.
  415.  *
  416.  * \param  s      selection object
  417.  * \param  mouse  state of mouse buttons and modifier keys
  418.  * \param  idx    byte offset within text representation
  419.  */
  420.  
  421. void selection_track(struct selection *s, browser_mouse_state mouse,
  422.                 unsigned idx)
  423. {
  424.         if (!SAME_SPACE(s, idx))
  425.                 return;
  426.  
  427.         if (!mouse) {
  428.                 s->drag_state = DRAG_NONE;
  429.         }
  430.  
  431.         switch (s->drag_state) {
  432.  
  433.                 case DRAG_START:
  434.                         if (idx > s->end_idx) {
  435.                                 unsigned old_end = s->end_idx;
  436.                                 selection_set_end(s, idx);
  437.                                 selection_set_start(s, old_end);
  438.                                 s->drag_state = DRAG_END;
  439.                         }
  440.                         else
  441.                                 selection_set_start(s, idx);
  442.                         break;
  443.  
  444.                 case DRAG_END:
  445.                         if (idx < s->start_idx) {
  446.                                 unsigned old_start = s->start_idx;
  447.                                 selection_set_start(s, idx);
  448.                                 selection_set_end(s, old_start);
  449.                                 s->drag_state = DRAG_START;
  450.                         }
  451.                         else
  452.                                 selection_set_end(s, idx);
  453.                         break;
  454.  
  455.                 default:
  456.                         break;
  457.         }
  458. }
  459.  
  460.  
  461. /**
  462.  * Tests whether a text box lies partially within the given range of
  463.  * byte offsets, returning the start and end indexes of the bytes
  464.  * that are enclosed.
  465.  *
  466.  * \param  box           box to be tested
  467.  * \param  start_idx     byte offset of start of range
  468.  * \param  end_idx       byte offset of end of range
  469.  * \param  start_offset  receives the start offset of the selected part
  470.  * \param  end_offset    receives the end offset of the selected part
  471.  * \return true iff the range encloses at least part of the box
  472.  */
  473.  
  474. bool selected_part(struct box *box, unsigned start_idx, unsigned end_idx,
  475.                 unsigned *start_offset, unsigned *end_offset)
  476. {
  477.         size_t box_length = box->length + SPACE_LEN(box);
  478.  
  479.         if (box_length > 0) {
  480.                 if (box->byte_offset >= start_idx &&
  481.                         box->byte_offset + box_length <= end_idx) {
  482.  
  483.                         /* fully enclosed */
  484.                         *start_offset = 0;
  485.                         *end_offset = box_length;
  486.                         return true;
  487.                 }
  488.                 else if (box->byte_offset + box_length > start_idx &&
  489.                         box->byte_offset < end_idx) {
  490.                         /* partly enclosed */
  491.                         int offset = 0;
  492.                         int len;
  493.  
  494.                         if (box->byte_offset < start_idx)
  495.                                 offset = start_idx - box->byte_offset;
  496.  
  497.                         len = box_length - offset;
  498.  
  499.                         if (box->byte_offset + box_length > end_idx)
  500.                                 len = end_idx - (box->byte_offset + offset);
  501.  
  502.                         *start_offset = offset;
  503.                         *end_offset = offset + len;
  504.  
  505.                         return true;
  506.                 }
  507.         }
  508.         return false;
  509. }
  510.  
  511.  
  512. /**
  513.  * Traverse the given box subtree, calling the handler function (with its handle)
  514.  * for all boxes that lie (partially) within the given range
  515.  *
  516.  * \param  box        box subtree
  517.  * \param  start_idx  start of range within textual representation (bytes)
  518.  * \param  end_idx    end of range
  519.  * \param  num_space  number space of the selection
  520.  * \param  handler    handler function to call
  521.  * \param  handle     handle to pass
  522.  * \param  before     type of whitespace to place before next encountered text
  523.  * \param  first      whether this is the first box with text
  524.  * \param  do_marker  whether deal enter any marker box
  525.  * \return false iff traversal abandoned part-way through
  526.  */
  527.  
  528. bool traverse_tree(struct box *box, unsigned start_idx, unsigned end_idx,
  529.                 unsigned int num_space, seln_traverse_handler handler,
  530.                 void *handle, save_text_whitespace *before, bool *first,
  531.                 bool do_marker)
  532. {
  533.         struct box *child;
  534.         const char *whitespace_text = "";
  535.         size_t whitespace_length = 0;
  536.  
  537.         assert(box);
  538.  
  539.         /* If selection starts inside marker */
  540.         if (box->parent && box->parent->list_marker == box && !do_marker) {
  541.                 /* set box to main list element */
  542.                 box = box->parent;
  543.         }
  544.  
  545.         /* If box has a list marker */
  546.         if (box->list_marker) {
  547.                 /* do the marker box before continuing with the rest of the
  548.                  * list element */
  549.                 if (!traverse_tree(box->list_marker, start_idx, end_idx,
  550.                                 num_space, handler, handle, before, first,
  551.                                 true))
  552.                         return false;
  553.         }
  554.  
  555.         /* we can prune this subtree, it's after the selection */
  556.         if (box->byte_offset >= end_idx)
  557.                 return true;
  558.  
  559.         /* read before calling the handler in case it modifies the tree */
  560.         child = box->children;
  561.  
  562.         /* If nicely formatted output of the selected text is required, work
  563.          * out what whitespace should be placed before the next bit of text */
  564.         if (before) {
  565.                 save_text_solve_whitespace(box, first, before, &whitespace_text,
  566.                                 &whitespace_length);
  567.         }
  568.         else {
  569.                 whitespace_text = NULL;
  570.         }
  571.         if (num_space == NUMBER_SPACE(box->byte_offset) &&
  572.                         box->type != BOX_BR &&
  573.                         !((box->type == BOX_FLOAT_LEFT ||
  574.                         box->type == BOX_FLOAT_RIGHT) &&
  575.                         !box->text)) {
  576.                 unsigned start_offset;
  577.                 unsigned end_offset;
  578.  
  579.                 if (selected_part(box, start_idx, end_idx, &start_offset,
  580.                                 &end_offset)) {
  581.                         if (!handler(box->text + start_offset, min(box->length,
  582.                                         end_offset) - start_offset,
  583.                                         box, handle, whitespace_text,
  584.                                         whitespace_length))
  585.                                 return false;
  586.                         if (before) {
  587.                                 *first = false;
  588.                                 *before = WHITESPACE_NONE;
  589.                         }
  590.                 }
  591.         }
  592.  
  593.         /* find the first child that could lie partially within the selection;
  594.          * this is important at the top-levels of the tree for pruning subtrees
  595.          * that lie entirely before the selection */
  596.  
  597.         if (child) {
  598.                 struct box *next = child->next;
  599.  
  600.                 while (next && next->byte_offset < start_idx) {
  601.                         child = next;
  602.                         next = child->next;
  603.                 }
  604.  
  605.                 while (child) {
  606.                         /* read before calling the handler in case it modifies
  607.                          * the tree */
  608.                         struct box *next = child->next;
  609.  
  610.                         if (!traverse_tree(child, start_idx, end_idx, num_space,
  611.                                         handler, handle, before, first, false))
  612.                                 return false;
  613.  
  614.                         child = next;
  615.                 }
  616.         }
  617.  
  618.         return true;
  619. }
  620.  
  621.  
  622. /**
  623.  * Traverse the current selection, calling the handler function (with its
  624.  * handle) for all boxes that lie (partially) within the given range
  625.  *
  626.  * \param  handler  handler function to call
  627.  * \param  handle   handle to pass
  628.  * \return false iff traversal abandoned part-way through
  629.  */
  630.  
  631. static bool selection_traverse(struct selection *s,
  632.                 seln_traverse_handler handler, void *handle)
  633. {
  634.         save_text_whitespace before = WHITESPACE_NONE;
  635.         bool first = true;
  636.         const char *text;
  637.         size_t length;
  638.  
  639.         if (!selection_defined(s))
  640.                 return true;    /* easy case, nothing to do */
  641.  
  642.         if (s->root) {
  643.                 /* HTML */
  644.                 return traverse_tree(s->root, s->start_idx, s->end_idx,
  645.                                 NUMBER_SPACE(s->max_idx), handler, handle,
  646.                                 &before, &first, false);
  647.         }
  648.  
  649.         /* Text */
  650.         text = textplain_get_raw_data(s->c, s->start_idx, s->end_idx, &length);
  651.  
  652.         if (text && !handler(text, length, NULL, handle, NULL, 0))
  653.                 return false;
  654.  
  655.         return true;
  656. }
  657.  
  658.  
  659. /**
  660.  * Selection traversal handler for redrawing the screen when the selection
  661.  * has been altered.
  662.  *
  663.  * \param  text         pointer to text string
  664.  * \param  length       length of text to be appended (bytes)
  665.  * \param  box          pointer to text box being (partially) added
  666.  * \param  handle       unused handle, we don't need one
  667.  * \param  whitespace_text    whitespace to place before text for formatting
  668.  *                            may be NULL
  669.  * \param  whitespace_length  length of whitespace_text
  670.  * \return true iff successful and traversal should continue
  671.  */
  672.  
  673. bool redraw_handler(const char *text, size_t length, struct box *box,
  674.                 void *handle, const char *whitespace_text,
  675.                 size_t whitespace_length)
  676. {
  677.         if (box) {
  678.                 struct rdw_info *r = (struct rdw_info*)handle;
  679.                 int width, height;
  680.                 int x, y;
  681.                 plot_font_style_t fstyle;
  682.  
  683.                 font_plot_style_from_css(box->style, &fstyle);
  684.  
  685.                 /* \todo - it should be possible to reduce the redrawn area by
  686.                  * considering the 'text', 'length' and 'space' parameters */
  687.                 box_coords(box, &x, &y);
  688.  
  689.                 width = box->padding[LEFT] + box->width + box->padding[RIGHT];
  690.                 height = box->padding[TOP] + box->height + box->padding[BOTTOM];
  691.  
  692.                 if (box->type == BOX_TEXT && box->space != 0)
  693.                         width += box->space;
  694.  
  695.                 if (r->inited) {
  696.                         if (x < r->r.x0) r->r.x0 = x;
  697.                         if (y < r->r.y0) r->r.y0 = y;
  698.                         if (x + width > r->r.x1) r->r.x1 = x + width;
  699.                         if (y + height > r->r.y1) r->r.y1 = y + height;
  700.                 }
  701.                 else {
  702.                         r->inited = true;
  703.                         r->r.x0 = x;
  704.                         r->r.y0 = y;
  705.                         r->r.x1 = x + width;
  706.                         r->r.y1 = y + height;
  707.                 }
  708.         }
  709.         return true;
  710. }
  711.  
  712.  
  713. /**
  714.  * Redraws the given range of text.
  715.  *
  716.  * \param  s          selection object
  717.  * \param  start_idx  start offset (bytes) within the textual representation
  718.  * \param  end_idx    end offset (bytes) within the textual representation
  719.  */
  720.  
  721. void selection_redraw(struct selection *s, unsigned start_idx, unsigned end_idx)
  722. {
  723.         struct rdw_info rdw;
  724.  
  725.         assert(end_idx >= start_idx);
  726.         rdw.inited = false;
  727.  
  728.         if (s->root) {
  729.                 if (!traverse_tree(s->root, start_idx, end_idx,
  730.                                 NUMBER_SPACE(s->max_idx), redraw_handler, &rdw,
  731.                                 NULL, NULL, false))
  732.                         return;
  733.         }
  734.         else {
  735.                 if (s->is_html == false && end_idx > start_idx) {
  736.                         textplain_coords_from_range(s->c, start_idx,
  737.                                                         end_idx, &rdw.r);
  738.                         rdw.inited = true;
  739.                 }
  740.         }
  741.  
  742.         if (rdw.inited)
  743.                 content__request_redraw(s->c, rdw.r.x0, rdw.r.y0,
  744.                                 rdw.r.x1 - rdw.r.x0, rdw.r.y1 - rdw.r.y0);
  745. }
  746.  
  747.  
  748. /**
  749.  * Append text to selection string.
  750.  *
  751.  * \param  text        text to be added
  752.  * \param  length      length of text in bytes
  753.  * \param  space       indicates whether a trailing space should be appended
  754.  * \param  sel_string  string to append to, may be resized
  755.  * \return true iff successful
  756.  */
  757.  
  758. static bool selection_string_append(const char *text, size_t length, bool space,
  759.                 plot_font_style_t *style, struct selection_string *sel_string)
  760. {
  761.         size_t new_length = sel_string->length + length + (space ? 1 : 0) + 1;
  762.  
  763.         if (style != NULL) {
  764.                 /* Add text run style */
  765.                 nsclipboard_styles *new_styles;
  766.  
  767.                 if (sel_string->n_styles == 0)
  768.                         assert(sel_string->length == 0);
  769.  
  770.                 new_styles = realloc(sel_string->styles,
  771.                                 (sel_string->n_styles + 1) *
  772.                                 sizeof(nsclipboard_styles));
  773.                 if (new_styles == NULL)
  774.                         return false;
  775.  
  776.                 sel_string->styles = new_styles;
  777.  
  778.                 sel_string->styles[sel_string->n_styles].style = *style;
  779.                 sel_string->styles[sel_string->n_styles].start =
  780.                                 sel_string->length;
  781.  
  782.                 sel_string->n_styles++;
  783.         }
  784.  
  785.         if (new_length > sel_string->buffer_len) {
  786.                 /* Need to extend buffer */
  787.                 size_t new_alloc = new_length + (new_length / 4);
  788.                 char *new_buff;
  789.  
  790.                 new_buff = realloc(sel_string->buffer, new_alloc);
  791.                 if (new_buff == NULL)
  792.                         return false;
  793.  
  794.                 sel_string->buffer = new_buff;
  795.                 sel_string->buffer_len = new_alloc;
  796.         }
  797.  
  798.         /* Copy text onto end of existing text in buffer */
  799.         memcpy(sel_string->buffer + sel_string->length, text, length);
  800.         sel_string->length += length;
  801.  
  802.         if (space)
  803.                 sel_string->buffer[sel_string->length++] = ' ';
  804.  
  805.         /* Ensure NULL termination */
  806.         sel_string->buffer[sel_string->length] = '\0';
  807.  
  808.         return true;
  809. }
  810.  
  811.  
  812. /**
  813.  * Selection traversal routine for appending text to a string
  814.  *
  815.  * \param  text         pointer to text being added, or NULL for newline
  816.  * \param  length       length of text to be appended (bytes)
  817.  * \param  box          pointer to text box, or NULL if from textplain
  818.  * \param  handle       selection string to append to
  819.  * \param  whitespace_text    whitespace to place before text for formatting
  820.  *                            may be NULL
  821.  * \param  whitespace_length  length of whitespace_text
  822.  * \return true iff successful and traversal should continue
  823.  */
  824.  
  825. static bool selection_copy_handler(const char *text, size_t length,
  826.                 struct box *box, void *handle, const char *whitespace_text,
  827.                 size_t whitespace_length)
  828. {
  829.         bool add_space = false;
  830.         plot_font_style_t style;
  831.         plot_font_style_t *pstyle = NULL;
  832.  
  833.         /* add any whitespace which precedes the text from this box */
  834.         if (whitespace_text != NULL && whitespace_length > 0) {
  835.                 if (!selection_string_append(whitespace_text,
  836.                                 whitespace_length, false, pstyle, handle)) {
  837.                         return false;
  838.                 }
  839.         }
  840.  
  841.         if (box != NULL) {
  842.                 /* HTML */
  843.                 add_space = (box->space != 0);
  844.  
  845.                 if (box->style != NULL) {
  846.                         /* Override default font style */
  847.                         font_plot_style_from_css(box->style, &style);
  848.                         pstyle = &style;
  849.                 } else {
  850.                         /* If there's no style, there must be no text */
  851.                         assert(box->text == NULL);
  852.                 }
  853.         }
  854.  
  855.         /* add the text from this box */
  856.         if (!selection_string_append(text, length, add_space, pstyle, handle))
  857.                 return false;
  858.  
  859.         return true;
  860. }
  861.  
  862.  
  863. /**
  864.  * Get copy of selection as string
  865.  *
  866.  * \param s  selection
  867.  * \return string of selected text, or NULL.  Ownership passed to caller.
  868.  */
  869.  
  870. char *selection_get_copy(struct selection *s)
  871. {
  872.         struct selection_string sel_string = {
  873.                 .buffer = NULL,
  874.                 .buffer_len = 0,
  875.                 .length = 0,
  876.  
  877.                 .n_styles = 0,
  878.                 .styles = NULL
  879.         };
  880.  
  881.         if (s == NULL || !s->defined)
  882.                 return NULL;
  883.  
  884.         if (!selection_traverse(s, selection_copy_handler, &sel_string)) {
  885.                 free(sel_string.buffer);
  886.                 free(sel_string.styles);
  887.                 return NULL;
  888.         }
  889.  
  890.         free(sel_string.styles);
  891.  
  892.         return sel_string.buffer;
  893. }
  894.  
  895.  
  896.  
  897. /**
  898.  * Copy the selected contents to the clipboard
  899.  *
  900.  * \param s  selection
  901.  * \return true iff successful
  902.  */
  903. bool selection_copy_to_clipboard(struct selection *s)
  904. {
  905.         struct selection_string sel_string = {
  906.                 .buffer = NULL,
  907.                 .buffer_len = 0,
  908.                 .length = 0,
  909.  
  910.                 .n_styles = 0,
  911.                 .styles = NULL
  912.         };
  913.  
  914.         if (s == NULL || !s->defined)
  915.                 return false;
  916.  
  917.         if (!selection_traverse(s, selection_copy_handler, &sel_string)) {
  918.                 free(sel_string.buffer);
  919.                 free(sel_string.styles);
  920.                 return false;
  921.         }
  922.  
  923.         gui_set_clipboard(sel_string.buffer, sel_string.length,
  924.                         sel_string.styles, sel_string.n_styles);
  925.  
  926.         free(sel_string.buffer);
  927.         free(sel_string.styles);
  928.  
  929.         return true;
  930. }
  931.  
  932.  
  933. /**
  934.  * Clears the current selection, optionally causing the screen to be updated.
  935.  *
  936.  * \param  s       selection object
  937.  * \param  redraw  true iff the previously selected region of the browser
  938.  *                window should be redrawn
  939.  */
  940.  
  941. void selection_clear(struct selection *s, bool redraw)
  942. {
  943.         int old_start, old_end;
  944.         bool was_defined;
  945.         struct browser_window *top = selection_get_browser_window(s);
  946.  
  947.         assert(s);
  948.         was_defined = selection_defined(s);
  949.         old_start = s->start_idx;
  950.         old_end = s->end_idx;
  951.  
  952.         s->defined = false;
  953.         s->start_idx = 0;
  954.         s->end_idx = 0;
  955.  
  956.         if (!top)
  957.                 return;
  958.  
  959.         top = browser_window_get_root(top);
  960.  
  961.         gui_clear_selection(top->window);
  962.  
  963.         if (redraw && was_defined)
  964.                 selection_redraw(s, old_start, old_end);
  965. }
  966.  
  967.  
  968. /**
  969.  * Selects all the text within the box subtree controlled by
  970.  * this selection object, updating the screen accordingly.
  971.  *
  972.  * \param  s  selection object
  973.  */
  974.  
  975. void selection_select_all(struct selection *s)
  976. {
  977.         assert(s);
  978.         s->defined = true;
  979.        
  980.         if (IS_INPUT(s->root))
  981.                 selection_set_start(s, s->root->children->children->byte_offset);
  982.         else
  983.                 selection_set_start(s, 0);
  984.         selection_set_end(s, s->max_idx);
  985. }
  986.  
  987.  
  988. /**
  989.  * Set the start position of the current selection, updating the screen.
  990.  *
  991.  * \param  s       selection object
  992.  * \param  offset  byte offset within textual representation
  993.  */
  994.  
  995. void selection_set_start(struct selection *s, unsigned offset)
  996. {
  997.         bool was_defined = selection_defined(s);
  998.         unsigned old_start = s->start_idx;
  999.        
  1000.         s->start_idx = offset;
  1001.         s->defined = (s->start_idx < s->end_idx);
  1002.        
  1003.         if (s->defined && s->root && s->root->gadget) {
  1004.                 /* update the caret text_box and offset so that it stays at the
  1005.                  * beginning of the selection */
  1006.                 s->root->gadget->caret_text_box = selection_get_start(s,
  1007.                                 &s->root->gadget->caret_box_offset);
  1008.                 assert(s->root->gadget->caret_text_box != NULL);
  1009.         }
  1010.  
  1011.         if (was_defined) {
  1012.                 if (offset < old_start)
  1013.                         selection_redraw(s, s->start_idx, old_start);
  1014.                 else
  1015.                         selection_redraw(s, old_start, s->start_idx);
  1016.         }
  1017.         else if (selection_defined(s))
  1018.                 selection_redraw(s, s->start_idx, s->end_idx);
  1019. }
  1020.  
  1021.  
  1022. /**
  1023.  * Set the end position of the current selection, updating the screen.
  1024.  *
  1025.  * \param  s       selection object
  1026.  * \param  offset  byte offset within textual representation
  1027.  */
  1028.  
  1029. void selection_set_end(struct selection *s, unsigned offset)
  1030. {
  1031.         bool was_defined = selection_defined(s);
  1032.         unsigned old_end = s->end_idx;
  1033.  
  1034.         s->end_idx = offset;
  1035.         s->defined = (s->start_idx < s->end_idx);
  1036.  
  1037.         if (was_defined) {
  1038.                 if (offset < old_end)
  1039.                         selection_redraw(s, s->end_idx, old_end);
  1040.                 else
  1041.                         selection_redraw(s, old_end, s->end_idx);
  1042.         }
  1043.         else if (selection_defined(s))
  1044.                 selection_redraw(s, s->start_idx, s->end_idx);
  1045. }
  1046.  
  1047.  
  1048. /**
  1049.  * Get the box and index of the specified byte offset within the
  1050.  * textual representation.
  1051.  *
  1052.  * \param  b       root node of search
  1053.  * \param  offset  byte offset within textual representation
  1054.  * \param  pidx    receives byte index of selection start point within box
  1055.  * \return ptr to box, or NULL if no selection defined
  1056.  */
  1057.  
  1058. struct box *get_box(struct box *b, unsigned offset, size_t *pidx)
  1059. {
  1060.         struct box *child = b->children;
  1061.  
  1062.         if (b->text) {
  1063.  
  1064.                 if (offset >= b->byte_offset &&
  1065.                         offset <= b->byte_offset + b->length + SPACE_LEN(b)) {
  1066.  
  1067.                         /* it's in this box */
  1068.                         *pidx = offset - b->byte_offset;
  1069.                         return b;
  1070.                 }
  1071.         }
  1072.  
  1073.         /* find the first child that could contain this offset */
  1074.         if (child) {
  1075.                 struct box *next = child->next;
  1076.                 while (next && next->byte_offset < offset) {
  1077.                         child = next;
  1078.                         next = child->next;
  1079.                 }
  1080.                 return get_box(child, offset, pidx);
  1081.         }
  1082.  
  1083.         return NULL;
  1084. }
  1085.  
  1086.  
  1087. /**
  1088.  * Get the box and index of the selection start, if defined.
  1089.  *
  1090.  * \param  s     selection object
  1091.  * \param  pidx  receives byte index of selection start point within box
  1092.  * \return ptr to box, or NULL if no selection defined
  1093.  */
  1094.  
  1095. struct box *selection_get_start(struct selection *s, size_t *pidx)
  1096. {
  1097.         return (s->defined ? get_box(s->root, s->start_idx, pidx) : NULL);
  1098. }
  1099.  
  1100.  
  1101. /**
  1102.  * Get the box and index of the selection end, if defined.
  1103.  *
  1104.  * \param  s     selection object
  1105.  * \param  pidx  receives byte index of selection end point within box
  1106.  * \return ptr to box, or NULL if no selection defined.
  1107.  */
  1108.  
  1109. struct box *selection_get_end(struct selection *s, size_t *pidx)
  1110. {
  1111.         return (s->defined ? get_box(s->root, s->end_idx, pidx) : NULL);
  1112. }
  1113.  
  1114.  
  1115. /**
  1116.  * Tests whether a text range lies partially within the selection, if there is
  1117.  * a selection defined, returning the start and end indexes of the bytes
  1118.  * that should be selected.
  1119.  *
  1120.  * \param  s          the selection object
  1121.  * \param  start      byte offset of start of text
  1122.  * \param  start_idx  receives the start index (in bytes) of the highlighted portion
  1123.  * \param  end_idx    receives the end index (in bytes)
  1124.  * \return true iff part of the given box lies within the selection
  1125.  */
  1126.  
  1127. bool selection_highlighted(const struct selection *s,
  1128.                 unsigned start, unsigned end,
  1129.                 unsigned *start_idx, unsigned *end_idx)
  1130. {
  1131.         /* caller should have checked first for efficiency */
  1132.         assert(s);
  1133.         assert(selection_defined(s));
  1134.  
  1135.         if (end <= s->start_idx || start >= s->end_idx)
  1136.                 return false;
  1137.  
  1138.         *start_idx = (s->start_idx >= start) ? (s->start_idx - start) : 0;
  1139.         *end_idx = min(end, s->end_idx) - start;
  1140.  
  1141.         return true;
  1142.  
  1143. //      assert(box);
  1144. //      assert(IS_TEXT(box));
  1145.  
  1146. //      return selected_part(box, s->start_idx, s->end_idx, start_idx, end_idx);
  1147. }
  1148.  
  1149.  
  1150. /**
  1151.  * Selection traversal handler for saving the text to a file.
  1152.  *
  1153.  * \param  text         pointer to text being added, or NULL for newline
  1154.  * \param  length       length of text to be appended (bytes)
  1155.  * \param  box          pointer to text box (or NULL for textplain content)
  1156.  * \param  handle       our save_state workspace pointer
  1157.  * \param  whitespace_text    whitespace to place before text for formatting
  1158.  *                            may be NULL
  1159.  * \param  whitespace_length  length of whitespace_text
  1160.  * \return true iff the file writing succeeded and traversal should continue.
  1161.  */
  1162.  
  1163. bool save_handler(const char *text, size_t length, struct box *box,
  1164.                 void *handle, const char *whitespace_text,
  1165.                 size_t whitespace_length)
  1166. {
  1167.         struct save_text_state *sv = handle;
  1168.         size_t new_length;
  1169.         int space = 0;
  1170.  
  1171.         assert(sv);
  1172.  
  1173.         if (box && (box->space > 0))
  1174.                 space = 1;
  1175.  
  1176.         if (whitespace_text)
  1177.                 length += whitespace_length;
  1178.  
  1179.         new_length = sv->length + whitespace_length + length + space;
  1180.         if (new_length >= sv->alloc) {
  1181.                 size_t new_alloc = sv->alloc + (sv->alloc / 4);
  1182.                 char *new_block;
  1183.  
  1184.                 if (new_alloc < new_length) new_alloc = new_length;
  1185.  
  1186.                 new_block = realloc(sv->block, new_alloc);
  1187.                 if (!new_block) return false;
  1188.  
  1189.                 sv->block = new_block;
  1190.                 sv->alloc = new_alloc;
  1191.         }
  1192.         if (whitespace_text) {
  1193.                 memcpy(sv->block + sv->length, whitespace_text,
  1194.                                 whitespace_length);
  1195.         }
  1196.         memcpy(sv->block + sv->length + whitespace_length, text, length);
  1197.         sv->length += length;
  1198.  
  1199.         if (space == 1)
  1200.                 sv->block[sv->length++] = ' ';
  1201.  
  1202.         return true;
  1203. }
  1204.  
  1205.  
  1206. /**
  1207.  * Save the given selection to a file.
  1208.  *
  1209.  * \param  s     selection object
  1210.  * \param  path  pathname to be used
  1211.  * \return true iff the save succeeded
  1212.  */
  1213.  
  1214. bool selection_save_text(struct selection *s, const char *path)
  1215. {
  1216.         struct save_text_state sv = { NULL, 0, 0 };
  1217.         utf8_convert_ret ret;
  1218.         char *result;
  1219.         FILE *out;
  1220.  
  1221.         if (!selection_traverse(s, save_handler, &sv)) {
  1222.                 free(sv.block);
  1223.                 return false;
  1224.         }
  1225.  
  1226.         if (!sv.block)
  1227.                 return false;
  1228.  
  1229.         ret = utf8_to_local_encoding(sv.block, sv.length, &result);
  1230.         free(sv.block);
  1231.  
  1232.         if (ret != UTF8_CONVERT_OK) {
  1233.                 LOG(("failed to convert to local encoding, return %d", ret));
  1234.                 return false;
  1235.         }
  1236.  
  1237.         out = fopen(path, "w");
  1238.         if (out) {
  1239.                 int res = fputs(result, out);
  1240.                 if (res < 0) {
  1241.                         LOG(("Warning: writing data failed"));
  1242.                 }
  1243.  
  1244.                 res = fputs("\n", out);
  1245.                 fclose(out);
  1246.                 free(result);
  1247.                 return (res != EOF);
  1248.         }
  1249.         free(result);
  1250.  
  1251.         return false;
  1252. }
  1253.  
  1254.  
  1255. /**
  1256.  * Adjust the selection to reflect a change in the selected text,
  1257.  * eg. editing in a text area/input field.
  1258.  *
  1259.  * \param  s            selection object
  1260.  * \param  byte_offset  byte offset of insertion/removal point
  1261.  * \param  change       byte size of change, +ve = insertion, -ve = removal
  1262.  * \param  redraw       true iff the screen should be updated
  1263.  */
  1264.  
  1265. void selection_update(struct selection *s, size_t byte_offset,
  1266.                 int change, bool redraw)
  1267. {
  1268.         if (selection_defined(s) &&
  1269.                 byte_offset >= s->start_idx &&
  1270.                 byte_offset < s->end_idx)
  1271.         {
  1272.                 if (change > 0)
  1273.                         s->end_idx += change;
  1274.                 else
  1275.                         s->end_idx +=
  1276.                                 max(change, (int)(byte_offset - s->end_idx));
  1277.         }
  1278. }
  1279.