Subversion Repositories Kolibri OS

Rev

Rev 3584 | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * Copyright 2006 James Bursa <bursa@users.sourceforge.net>
  3.  * Copyright 2005 Richard Wilson <info@tinct.net>
  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.  * Browser history tree (implementation).
  22.  */
  23.  
  24. #include <assert.h>
  25. #include <stdbool.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <time.h>
  29. #include "content/content.h"
  30. #include "content/hlcache.h"
  31. #include "content/urldb.h"
  32. #include "css/css.h"
  33. #include "desktop/browser.h"
  34. #include "desktop/gui.h"
  35. #include "desktop/history_core.h"
  36. #include "desktop/plotters.h"
  37. #include "desktop/thumbnail.h"
  38. #include "image/bitmap.h"
  39. #include "render/font.h"
  40. #include "utils/log.h"
  41. #include "utils/url.h"
  42. #include "utils/utils.h"
  43.  
  44.  
  45. #define WIDTH 100
  46. #define HEIGHT 86
  47. #define RIGHT_MARGIN 50
  48. #define BOTTOM_MARGIN 30
  49.  
  50. struct history_page {
  51.         char *url;    /**< Page URL, never 0. */
  52.         char *frag_id; /** Fragment identifier, or 0. */
  53.         char *title;  /**< Page title, never 0. */
  54. };
  55.  
  56. /** A node in the history tree. */
  57. struct history_entry {
  58.         struct history_page page;
  59.         struct history_entry *back;  /**< Parent. */
  60.         struct history_entry *next;  /**< Next sibling. */
  61.         struct history_entry *forward;  /**< First child. */
  62.         struct history_entry *forward_pref;  /**< Child in direction of
  63.                                                   current entry. */
  64.         struct history_entry *forward_last;  /**< Last child. */
  65.         unsigned int children;  /**< Number of children. */
  66.         int x;  /**< Position of node. */
  67.         int y;  /**< Position of node. */
  68.         struct bitmap *bitmap;  /**< Thumbnail bitmap, or 0. */
  69. };
  70.  
  71. /** History tree for a window. */
  72. struct history {
  73.         /** First page in tree (page that window opened with). */
  74.         struct history_entry *start;
  75.         /** Current position in tree. */
  76.         struct history_entry *current;
  77.         /** Width of layout. */
  78.         int width;
  79.         /** Height of layout. */
  80.         int height;
  81. };
  82.  
  83. static struct history_entry *history_clone_entry(struct history *history,
  84.                 struct history_entry *entry);
  85. static void history_free_entry(struct history_entry *entry);
  86. static void history_layout(struct history *history);
  87. static int history_layout_subtree(struct history *history,
  88.                 struct history_entry *entry, int x, int y, bool shuffle);
  89. static bool history_redraw_entry(struct history *history,
  90.                 struct history_entry *entry,
  91.                 int x0, int y0, int x1, int y1,
  92.                 int x, int y, bool clip, const struct redraw_context *ctx);
  93. static struct history_entry *history_find_position(struct history_entry *entry,
  94.                 int x, int y);
  95. static bool history_enumerate_entry(const struct history *history,
  96.                 const struct history_entry *entry, history_enumerate_cb cb, void *ud);
  97.  
  98.  
  99. /**
  100.  * Create a new history tree for a window.
  101.  *
  102.  * \return  pointer to an opaque history structure, 0 on failure.
  103.  */
  104.  
  105. struct history *history_create(void)
  106. {
  107.         struct history *history;
  108.  
  109.         history = calloc(1, sizeof *history);
  110.         if (!history) {
  111.                 warn_user("NoMemory", 0);
  112.                 return 0;
  113.         }
  114.         history->width = RIGHT_MARGIN / 2;
  115.         history->height = BOTTOM_MARGIN / 2;
  116.         return history;
  117. }
  118.  
  119.  
  120. /**
  121.  * Clone a history tree
  122.  *
  123.  * \param  history  opaque history structure, as returned by history_create()
  124.  *
  125.  * \return  pointer to an opaque history structure, 0 on failure.
  126.  */
  127.  
  128. struct history *history_clone(struct history *history)
  129. {
  130.         struct history *new_history;
  131.  
  132.         if (!history->start)
  133.                 return history_create();
  134.  
  135.         new_history = malloc(sizeof *history);
  136.         if (!new_history)
  137.                 return 0;
  138.         memcpy(new_history, history, sizeof *history);
  139.  
  140.         new_history->start = history_clone_entry(new_history,
  141.                         new_history->start);
  142.         if (!history->start) {
  143.                 LOG(("Insufficient memory to clone history"));
  144.                 warn_user("NoMemory", 0);
  145.                 history_destroy(new_history);
  146.                 return 0;
  147.         }
  148.  
  149.         return new_history;
  150. }
  151.  
  152.  
  153. /**
  154.  * Clone a history entry
  155.  *
  156.  * \param  history  opaque history structure, as returned by history_create()
  157.  * \param  start    entry to clone
  158.  *
  159.  * \return  a cloned history entry, or 0 on error
  160.  */
  161.  
  162. struct history_entry *history_clone_entry(struct history *history,
  163.                 struct history_entry *entry)
  164. {
  165.         struct history_entry *child;
  166.         struct history_entry *new_child;
  167.         struct history_entry *prev = NULL;
  168.         struct history_entry *new_entry;
  169.  
  170.         assert(entry->page.url);
  171.         assert(entry->page.title);
  172.  
  173.         /* clone the entry */
  174.         new_entry = malloc(sizeof *entry);
  175.         if (!new_entry)
  176.                 return 0;
  177.         memcpy(new_entry, entry, sizeof *entry);
  178.         new_entry->page.url = strdup(entry->page.url);
  179.         if (entry->page.frag_id)
  180.                 new_entry->page.frag_id = strdup(entry->page.frag_id);
  181.         new_entry->page.title = strdup(entry->page.title);
  182.         if (!new_entry->page.url || !new_entry->page.title ||
  183.                         ((entry->page.frag_id) && (!new_entry->page.frag_id))) {
  184.                 free(new_entry->page.url);
  185.                 free(new_entry->page.title);
  186.                 free(new_entry->page.frag_id);
  187.                 free(new_entry);
  188.                 return 0;
  189.         }
  190.  
  191.         /* update references */
  192.         if (history->current == entry)
  193.                 history->current = new_entry;
  194.  
  195.         /* recurse for all children */
  196.         for (child = new_entry->forward; child; child = child->next) {
  197.                 new_child = history_clone_entry(history, child);
  198.                 if (new_child)
  199.                         new_child->back = new_entry;
  200.                 if (prev)
  201.                         prev->next = new_child;
  202.                 if (new_entry->forward == child)
  203.                         new_entry->forward = new_child;
  204.                 if (new_entry->forward_pref == child)
  205.                         new_entry->forward_pref = new_child;
  206.                 if (new_entry->forward_last == child)
  207.                         new_entry->forward_last = new_child;
  208.                 if (!new_child)
  209.                         return 0;
  210.                 prev = new_child;
  211.         }
  212.         return new_entry;
  213. }
  214.  
  215.  
  216. /**
  217.  * Insert a url into the history tree.
  218.  *
  219.  * \param  history  opaque history structure, as returned by history_create()
  220.  * \param  content  content to add to history
  221.  * \param  frag_id  fragment identifier
  222.  *
  223.  * The page is added after the current entry and becomes current.
  224.  */
  225.  
  226. void history_add(struct history *history, hlcache_handle *content,
  227.                 const char *frag_id)
  228. {
  229.         struct history_entry *entry;
  230.         nsurl *nsurl = hlcache_handle_get_url(content);
  231.         char *url;
  232.         char *title;
  233.         struct bitmap *bitmap;
  234.         nserror error;
  235.         size_t url_len;
  236.  
  237.         assert(history);
  238.         assert(content);
  239.  
  240.         /* allocate space */
  241.         entry = malloc(sizeof *entry);
  242.         if (entry == NULL)
  243.                 return;
  244.  
  245.         /* TODO: use a nsurl? */
  246.         error = nsurl_get(nsurl, NSURL_WITH_FRAGMENT, &url, &url_len);
  247.         if (error != NSERROR_OK) {
  248.                 warn_user("NoMemory", 0);
  249.                 free(entry);
  250.                 return;
  251.         }
  252.  
  253.         title = strdup(content_get_title(content));
  254.         if (title == NULL) {
  255.                 warn_user("NoMemory", 0);
  256.                 free(url);
  257.                 free(entry);
  258.                 return;
  259.         }
  260.  
  261.         entry->page.url = url;
  262.         entry->page.frag_id = frag_id ? strdup(frag_id) : 0;
  263.         entry->page.title = title;
  264.         entry->back = history->current;
  265.         entry->next = 0;
  266.         entry->forward = entry->forward_pref = entry->forward_last = 0;
  267.         entry->children = 0;
  268.         entry->bitmap = 0;
  269.         if (history->current) {
  270.                 if (history->current->forward_last)
  271.                         history->current->forward_last->next = entry;
  272.                 else
  273.                         history->current->forward = entry;
  274.                 history->current->forward_pref = entry;
  275.                 history->current->forward_last = entry;
  276.                 history->current->children++;
  277.         } else {
  278.                 history->start = entry;
  279.         }
  280.         history->current = entry;
  281.  
  282.         /* if we have a thumbnail, don't update until the page has finished
  283.          * loading */
  284.         bitmap = urldb_get_thumbnail(nsurl);
  285.         if (!bitmap) {
  286.                 bitmap = bitmap_create(WIDTH, HEIGHT,
  287.                                 BITMAP_NEW | BITMAP_CLEAR_MEMORY |
  288.                                 BITMAP_OPAQUE);
  289.                 if (!bitmap) {
  290.                         warn_user("NoMemory", 0);
  291.                         return;
  292.                 }
  293.                 if (thumbnail_create(content, bitmap, nsurl) == false) {
  294.                         /* Thumbnailing failed. Ignore it silently */
  295.                         bitmap_destroy(bitmap);
  296.                         bitmap = NULL;
  297.                 }
  298.         }
  299.         entry->bitmap = bitmap;
  300.  
  301.         history_layout(history);
  302. }
  303.  
  304.  
  305. /**
  306.  * Update the thumbnail for the current entry.
  307.  *
  308.  * \param  history  opaque history structure, as returned by history_create()
  309.  * \param  content  content for current entry
  310.  */
  311.  
  312. void history_update(struct history *history, hlcache_handle *content)
  313. {
  314.         char *title;
  315.  
  316.         if (!history || !history->current || !history->current->bitmap)
  317.                 return;
  318.  
  319.         assert(history->current->page.url);
  320.         assert(history->current->page.title);
  321.  
  322.         title = strdup(content_get_title(content));
  323.         if (!title) {
  324.                 warn_user("NoMemory", 0);
  325.                 return;
  326.         }
  327.  
  328.         assert(title);
  329.         free(history->current->page.title);
  330.         history->current->page.title = title;
  331.  
  332.         thumbnail_create(content, history->current->bitmap, NULL);
  333. }
  334.  
  335.  
  336. /**
  337.  * Free a history structure.
  338.  *
  339.  * \param  history  opaque history structure, as returned by history_create()
  340.  */
  341.  
  342. void history_destroy(struct history *history)
  343. {
  344.         if (!history)
  345.                 return;
  346.         history_free_entry(history->start);
  347.         free(history);
  348. }
  349.  
  350.  
  351. /**
  352.  * Free an entry in the tree recursively.
  353.  */
  354.  
  355. void history_free_entry(struct history_entry *entry)
  356. {
  357.         if (!entry)
  358.                 return;
  359.         history_free_entry(entry->forward);
  360.         history_free_entry(entry->next);
  361.         free(entry->page.url);
  362.         if (entry->page.frag_id)
  363.                 free(entry->page.frag_id);
  364.         free(entry->page.title);
  365.         free(entry);
  366. }
  367.  
  368.  
  369. /**
  370.  * Go back in the history.
  371.  *
  372.  * \param  bw       browser window
  373.  * \param  history  history of the window
  374.  */
  375.  
  376. void history_back(struct browser_window *bw, struct history *history)
  377. {
  378.         if (!history || !history->current || !history->current->back)
  379.                 return;
  380.         history_go(bw, history, history->current->back, false);
  381. }
  382.  
  383.  
  384. /**
  385.  * Go forward in the history.
  386.  *
  387.  * \param  bw       browser window
  388.  * \param  history  history of the window
  389.  */
  390.  
  391. void history_forward(struct browser_window *bw, struct history *history)
  392. {
  393.         if (!history || !history->current || !history->current->forward_pref)
  394.                 return;
  395.         history_go(bw, history, history->current->forward_pref, false);
  396. }
  397.  
  398.  
  399. /**
  400.  * Check whether it is pssible to go back in the history.
  401.  *
  402.  * \param  history  history of the window
  403.  * \return true if the history can go back, false otherwise
  404.  */
  405.  
  406. bool history_back_available(struct history *history)
  407. {
  408.         return (history && history->current && history->current->back);
  409. }
  410.  
  411.  
  412. /**
  413.  * Check whether it is pssible to go forwards in the history.
  414.  *
  415.  * \param  history  history of the window
  416.  * \return true if the history can go forwards, false otherwise
  417.  */
  418.  
  419. bool history_forward_available(struct history *history)
  420. {
  421.         return (history && history->current && history->current->forward_pref);
  422. }
  423.  
  424.  
  425. /* Documented in history_core.h */
  426. void history_go(struct browser_window *bw, struct history *history,
  427.                 struct history_entry *entry, bool new_window)
  428. {
  429.         char *url;
  430.         struct history_entry *current;
  431.  
  432. //      LOG(("%p %p %p", bw, history, entry));
  433. //      LOG(("%s %s %s",
  434. //              entry->page.url, entry->page.title, entry->page.frag_id));
  435.  
  436.         if (entry->page.frag_id) {
  437.                 url = malloc(strlen(entry->page.url) +
  438.                                 strlen(entry->page.frag_id) + 5);
  439.                 if (!url) {
  440.                         warn_user("NoMemory", 0);
  441.                         return;
  442.                 }
  443.                 sprintf(url, "%s#%s", entry->page.url, entry->page.frag_id);
  444.         }
  445.         else
  446.                 url = entry->page.url;
  447.  
  448.         if (new_window) {
  449.                 current = history->current;
  450.                 history->current = entry;
  451.                 browser_window_create(url, bw, 0, false, false);
  452.                 history->current = current;
  453.         } else {
  454.                 history->current = entry;
  455.                 browser_window_go(bw, url, 0, false);
  456.         }
  457.  
  458.         if (entry->page.frag_id)
  459.                 free(url);
  460. }
  461.  
  462.  
  463. /**
  464.  * Compute node positions.
  465.  *
  466.  * \param  history  history to layout
  467.  *
  468.  * Each node's x and y are filled in.
  469.  */
  470.  
  471. void history_layout(struct history *history)
  472. {
  473.         time_t t = time(0);
  474.         struct tm *tp = localtime(&t);
  475.         bool shuffle = tp->tm_mon == 3 && tp->tm_mday == 1;
  476.  
  477.         if (!history)
  478.                 return;
  479.  
  480.         history->width = 0;
  481.         if (history->start)
  482.                 history->height = history_layout_subtree(history,
  483.                         history->start, RIGHT_MARGIN / 2, BOTTOM_MARGIN / 2,
  484.                         shuffle);
  485.         else
  486.                 history->height = 0;
  487.         if (shuffle) {
  488.                 history->width = 600 + WIDTH;
  489.                 history->height = 400 + HEIGHT;
  490.         }
  491.         history->width += RIGHT_MARGIN / 2;
  492.         history->height += BOTTOM_MARGIN / 2;
  493. }
  494.  
  495.  
  496. /**
  497.  * Recursively position a subtree.
  498.  *
  499.  * \param  history  history being laid out
  500.  * \param  entry    subtree to position
  501.  * \param  x        x position for entry
  502.  * \param  y        smallest available y
  503.  * \param  shuffle  shuffle layout
  504.  * \return  greatest y used by subtree
  505.  */
  506.  
  507. int history_layout_subtree(struct history *history,
  508.                 struct history_entry *entry, int x, int y, bool shuffle)
  509. {
  510.         struct history_entry *child;
  511.         int y1 = y;
  512.  
  513.         if (history->width < x + WIDTH)
  514.                 history->width = x + WIDTH;
  515.  
  516.         if (!entry->forward) {
  517.                 entry->x = x;
  518.                 entry->y = y;
  519.                 if (shuffle) {
  520.                         entry->x = rand() % 600;
  521.                         entry->y = rand() % 400;
  522.                 }
  523.                 return y + HEIGHT;
  524.         }
  525.  
  526.         /* layout child subtrees below each other */
  527.         for (child = entry->forward; child; child = child->next) {
  528.                 y1 = history_layout_subtree(history, child,
  529.                                 x + WIDTH + RIGHT_MARGIN, y1, shuffle);
  530.                 if (child->next)
  531.                         y1 += BOTTOM_MARGIN;
  532.         }
  533.  
  534.         /* place ourselves in the middle */
  535.         entry->x = x;
  536.         entry->y = (y + y1) / 2 - HEIGHT / 2;
  537.         if (shuffle) {
  538.                 entry->x = rand() % 600;
  539.                 entry->y = rand() % 400;
  540.         }
  541.  
  542.         return y1;
  543. }
  544.  
  545.  
  546. /**
  547.  * Get the dimensions of a history.
  548.  *
  549.  * \param  history  history to measure
  550.  * \param  width    updated to width
  551.  * \param  height   updated to height
  552.  */
  553.  
  554. void history_size(struct history *history, int *width, int *height)
  555. {
  556.         *width = history->width;
  557.         *height = history->height;
  558. }
  559.  
  560.  
  561. /**
  562.  * Redraw a history.
  563.  *
  564.  * \param history       history to render
  565.  * \param ctx           current redraw context
  566.  */
  567.  
  568. bool history_redraw(struct history *history, const struct redraw_context *ctx)
  569. {
  570.         if (!history->start)
  571.                 return true;
  572.         return history_redraw_entry(history, history->start, 0, 0, 0, 0, 0, 0,
  573.                         false, ctx);
  574. }
  575.  
  576. /**
  577.  * Redraw part of a history.
  578.  *
  579.  * \param  history      history to render
  580.  * \param  x0           left X co-ordinate of redraw area
  581.  * \param  y0           top Y co-ordinate of redraw area
  582.  * \param  x1           right X co-ordinate of redraw area
  583.  * \param  y1           lower Y co-ordinate of redraw area
  584.  * \param  x            start X co-ordinate on plot canvas
  585.  * \param  y            start Y co-ordinate on plot canvas
  586.  * \param ctx           current redraw context
  587.  */
  588.  
  589. bool history_redraw_rectangle(struct history *history,
  590.         int x0, int y0, int x1, int y1,
  591.         int x, int y, const struct redraw_context *ctx)
  592. {
  593.         if (!history->start)
  594.                 return true;
  595.         return history_redraw_entry(history, history->start,
  596.                 x0, y0, x1, y1, x, y, true, ctx);
  597. }
  598.  
  599. /**
  600.  * Recursively redraw a history_entry.
  601.  *
  602.  * \param  history        history containing the entry
  603.  * \param  history_entry  entry to render
  604.  * \param ctx             current redraw context
  605.  */
  606.  
  607. bool history_redraw_entry(struct history *history,
  608.                 struct history_entry *entry,
  609.                 int x0, int y0, int x1, int y1,
  610.                 int x, int y, bool clip, const struct redraw_context *ctx)
  611. {
  612.         const struct plotter_table *plot = ctx->plot;
  613.         size_t char_offset;
  614.         int actual_x;
  615.         struct history_entry *child;
  616.         colour c = entry == history->current ? HISTORY_COLOUR_SELECTED : HISTORY_COLOUR_FOREGROUND;
  617.         int tailsize = 5;
  618.         int xoffset = x - x0;
  619.         int yoffset = y - y0;
  620.         plot_style_t pstyle_history_rect = {
  621.             .stroke_type = PLOT_OP_TYPE_SOLID,
  622.             .stroke_colour = c,
  623.             .stroke_width = entry == history->current ? 3 : 1,
  624.         };
  625.         plot_font_style_t fstyle = *plot_style_font;
  626.  
  627.         if (clip) {
  628.                 struct rect rect;
  629.                 rect.x0 = x0 + xoffset;
  630.                 rect.y0 = y0 + yoffset;
  631.                 rect.x1 = x1 + xoffset;
  632.                 rect.y1 = y1 + yoffset;
  633.                 if(!plot->clip(&rect))
  634.                         return false;
  635.         }
  636.  
  637.         if (!plot->bitmap(entry->x + xoffset, entry->y + yoffset, WIDTH, HEIGHT,
  638.                         entry->bitmap, 0xffffff, 0))
  639.                 return false;
  640.         if (!plot->rectangle(entry->x - 1 + xoffset,
  641.                             entry->y - 1 + yoffset,
  642.                             entry->x + xoffset + WIDTH,
  643.                             entry->y + yoffset + HEIGHT,
  644.                             &pstyle_history_rect))
  645.                 return false;
  646.  
  647.         if (!nsfont.font_position_in_string(plot_style_font, entry->page.title,
  648.                         strlen(entry->page.title), WIDTH,
  649.                         &char_offset, &actual_x))
  650.                 return false;
  651.  
  652.         fstyle.background = HISTORY_COLOUR_BACKGROUND;
  653.         fstyle.foreground = c;
  654.         fstyle.weight = entry == history->current ? 900 : 400;
  655.  
  656.         if (!plot->text(entry->x + xoffset, entry->y + HEIGHT + 12 + yoffset,
  657.                         entry->page.title, char_offset, &fstyle))
  658.                 return false;
  659.  
  660.         for (child = entry->forward; child; child = child->next) {
  661.                 if (!plot->line(entry->x + WIDTH + xoffset,
  662.                                 entry->y + HEIGHT / 2 + yoffset,
  663.                         entry->x + WIDTH + tailsize + xoffset,
  664.                                 entry->y + HEIGHT / 2 + yoffset,
  665.                                plot_style_stroke_history))
  666.                         return false;
  667.                 if (!plot->line(entry->x + WIDTH + tailsize + xoffset,
  668.                                entry->y + HEIGHT / 2 + yoffset,
  669.                                child->x - tailsize +xoffset,
  670.                                child->y + HEIGHT / 2 + yoffset,
  671.                                plot_style_stroke_history))
  672.                         return false;
  673.                 if (!plot->line(child->x - tailsize + xoffset,
  674.                                child->y + HEIGHT / 2 + yoffset,
  675.                                child->x + xoffset, child->y + HEIGHT / 2 + yoffset,
  676.                                plot_style_stroke_history))
  677.                         return false;
  678.                 if (!history_redraw_entry(history, child, x0, y0, x1, y1, x, y,
  679.                                 clip, ctx))
  680.                         return false;
  681.         }
  682.  
  683.         return true;
  684. }
  685.  
  686.  
  687. /**
  688.  * Handle a mouse click in a history.
  689.  *
  690.  * \param  bw          browser window containing history
  691.  * \param  history     history that was clicked in
  692.  * \param  x           click coordinate
  693.  * \param  y           click coordinate
  694.  * \param  new_window  open a new window instead of using bw
  695.  * \return  true if action was taken, false if click was not on an entry
  696.  */
  697.  
  698. bool history_click(struct browser_window *bw, struct history *history,
  699.                 int x, int y, bool new_window)
  700. {
  701.         struct history_entry *entry;
  702.  
  703.         entry = history_find_position(history->start, x, y);
  704.         if (!entry)
  705.                 return false;
  706.         if (entry == history->current)
  707.                 return false;
  708.  
  709.         history_go(bw, history, entry, new_window);
  710.  
  711.         return true;
  712. }
  713.  
  714.  
  715. /**
  716.  * Determine the URL of the entry at a position.
  717.  *
  718.  * \param  history  history to search
  719.  * \param  x        coordinate
  720.  * \param  y        coordinate
  721.  * \return  URL, or 0 if no entry at (x, y)
  722.  */
  723.  
  724. const char *history_position_url(struct history *history, int x, int y)
  725. {
  726.         struct history_entry *entry;
  727.  
  728.         entry = history_find_position(history->start, x, y);
  729.         if (!entry)
  730.                 return 0;
  731.  
  732.         return entry->page.url;
  733. }
  734.  
  735.  
  736. /**
  737.  * Find the history entry at a position.
  738.  *
  739.  * \param  entry  entry to search from
  740.  * \param  x      coordinate
  741.  * \param  y      coordinate
  742.  * \return  an entry if found, 0 if none
  743.  */
  744.  
  745. struct history_entry *history_find_position(struct history_entry *entry,
  746.                 int x, int y)
  747. {
  748.         struct history_entry *child;
  749.         struct history_entry *found;
  750.  
  751.         if (!entry)
  752.                 return 0;
  753.  
  754.         if (entry->x <= x && x <= entry->x + WIDTH &&
  755.                         entry->y <= y && y <= entry->y + HEIGHT)
  756.                 return entry;
  757.  
  758.         for (child = entry->forward; child; child = child->next) {
  759.                 found = history_find_position(child, x, y);
  760.                 if (found)
  761.                         return found;
  762.         }
  763.  
  764.         return 0;
  765. }
  766.  
  767. /* Documented in history_core.h */
  768. void history_enumerate_forward(struct history *history,
  769.         history_enumerate_cb cb, void *user_data)
  770. {
  771.         struct history_entry *entry;
  772.        
  773.         if (history == NULL || history->current == NULL) return;
  774.        
  775.         for (entry = history->current->forward_pref; entry != NULL; entry = entry->forward_pref) {
  776.                 if (!cb(history, entry->x, entry->y, entry->x + WIDTH,
  777.                                 entry->y + HEIGHT, entry, user_data))
  778.                         break;
  779.         }
  780. }
  781.  
  782. /* Documented in history_core.h */
  783. void history_enumerate_back(struct history *history,
  784.         history_enumerate_cb cb, void *user_data)
  785. {
  786.         struct history_entry *entry;
  787.        
  788.         if (history == NULL || history->current == NULL) return;
  789.        
  790.         for (entry = history->current->back; entry != NULL; entry = entry->back) {
  791.                 if (!cb(history, entry->x, entry->y, entry->x + WIDTH,
  792.                                 entry->y + HEIGHT, entry, user_data))
  793.                         break;
  794.         }
  795. }
  796.  
  797. /* Documented in history_core.h */
  798. void history_enumerate(const struct history *history, history_enumerate_cb cb,
  799.         void *user_data)
  800. {
  801.         history_enumerate_entry(history, history->start, cb, user_data);
  802. }
  803.  
  804. /**
  805.  * Enumerate subentries in history
  806.  * See also history_enumerate()
  807.  *
  808.  * \param       history         history to enumerate
  809.  * \param       entry           entry to start enumeration at
  810.  * \param       cb                      callback function
  811.  * \param       ud                      context pointer passed to cb
  812.  * \return      true to continue enumeration, false to cancel
  813.  */
  814. static bool history_enumerate_entry(const struct history *history,
  815.         const struct history_entry *entry, history_enumerate_cb cb, void *ud)
  816. {
  817.         const struct history_entry *child;
  818.        
  819.         if (!cb(history, entry->x, entry->y, entry->x + WIDTH, entry->y + HEIGHT,
  820.                         entry, ud)) return false;
  821.        
  822.         for (child = entry->forward; child; child = child->next) {
  823.                 if (!history_enumerate_entry(history, child, cb, ud))
  824.                         return false;
  825.         }
  826.        
  827.         return true;
  828. }
  829.  
  830. /* Documented in history_core.h */
  831. const char *history_entry_get_url(const struct history_entry *entry)
  832. {
  833.         return entry->page.url;
  834. }
  835.  
  836. /* Documented in history_core.h */
  837. const char *history_entry_get_fragment_id(const struct history_entry *entry)
  838. {
  839.         return entry->page.frag_id;
  840. }
  841.  
  842. /* Documented in history_core.h */
  843. const char *history_entry_get_title(const struct history_entry *entry)
  844. {
  845.         return entry->page.title;
  846. }
  847.