diff options
Diffstat (limited to 'desktop/history_core.c')
-rw-r--r-- | desktop/history_core.c | 545 |
1 files changed, 545 insertions, 0 deletions
diff --git a/desktop/history_core.c b/desktop/history_core.c new file mode 100644 index 000000000..04e02d736 --- /dev/null +++ b/desktop/history_core.c @@ -0,0 +1,545 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2006 James Bursa <bursa@users.sourceforge.net> + * Copyright 2005 Richard Wilson <info@tinct.net> + */ + +/** \file + * Browser history tree (implementation). + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "netsurf/content/content.h" +#include "netsurf/content/url_store.h" +#include "netsurf/css/css.h" +#include "netsurf/desktop/gui.h" +#include "netsurf/desktop/history_core.h" +#include "netsurf/desktop/plotters.h" +#include "netsurf/image/bitmap.h" +#include "netsurf/render/font.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/url.h" +#include "netsurf/utils/utils.h" + + +#define WIDTH 100 +#define HEIGHT 86 +#define RIGHT_MARGIN 50 +#define BOTTOM_MARGIN 30 + + +/** A node in the history tree. */ +struct history_entry { + char *url; /**< Page URL. */ + char *frag_id; /** Fragment identifier */ + char *title; /**< Page title. */ + struct history_entry *back; /**< Parent. */ + struct history_entry *next; /**< Next sibling. */ + struct history_entry *forward; /**< First child. */ + struct history_entry *forward_pref; /**< Child in direction of + current entry. */ + struct history_entry *forward_last; /**< Last child. */ + unsigned int children; /**< Number of children. */ + int x; /**< Position of node. */ + int y; /**< Position of node. */ + struct bitmap *bitmap; /**< Thumbnail bitmap, or 0. */ +}; + +/** History tree for a window. */ +struct history { + /** First page in tree (page that window opened with). */ + struct history_entry *start; + /** Current position in tree. */ + struct history_entry *current; + /** Width of layout. */ + int width; + /** Height of layout. */ + int height; +}; + +static void history_free_entry(struct history_entry *entry); +static void history_go(struct browser_window *bw, struct history *history, + struct history_entry *entry, bool new_window); +static void history_layout(struct history *history); +static int history_layout_subtree(struct history *history, + struct history_entry *entry, int x, int y, bool shuffle); +static bool history_redraw_entry(struct history *history, + struct history_entry *entry); +static struct history_entry *history_find_position(struct history_entry *entry, + int x, int y); + + +/** + * Create a new history tree for a window. + * + * \return pointer to an opaque history structure, 0 on failure. + */ + +struct history *history_create(void) +{ + struct history *history; + + history = malloc(sizeof *history); + if (!history) { + warn_user("NoMemory", 0); + return 0; + } + + history->start = 0; + history->current = 0; + + return history; +} + + +/** + * Insert a url into the history tree. + * + * \param history opaque history structure, as returned by history_create() + * \param content content to add to history + * \param frag_id fragment identifier + * + * The page is added after the current entry and becomes current. + */ + +void history_add(struct history *history, struct content *content, + char *frag_id) +{ + url_func_result res; + struct history_entry *entry; + char *url; + char *title; + struct bitmap *bitmap; + + assert(history); + assert(content); + + /* allocate space */ + entry = malloc(sizeof *entry); + res = url_normalize(content->url, &url); + if (res != URL_FUNC_OK) { + warn_user("NoMemory", 0); + return; + } + title = strdup(content->title ? content->title : url); + if (!entry || !url || !title) { + warn_user("NoMemory", 0); + free(entry); + free(url); + free(title); + return; + } + + entry->url = url; + entry->frag_id = frag_id ? strdup(frag_id) : 0; + entry->title = title; + entry->back = history->current; + entry->next = 0; + entry->forward = entry->forward_pref = entry->forward_last = 0; + entry->children = 0; + entry->bitmap = 0; + if (history->current) { + if (history->current->forward_last) + history->current->forward_last->next = entry; + else + history->current->forward = entry; + history->current->forward_pref = entry; + history->current->forward_last = entry; + history->current->children++; + } else { + history->start = entry; + } + history->current = entry; + + /* if we have a thumbnail, don't update until the page has finished + * loading */ + bitmap = url_store_get_thumbnail(url); + if (!bitmap) { + bitmap = bitmap_create(WIDTH, HEIGHT, + BITMAP_NEW | BITMAP_CLEAR_MEMORY | + BITMAP_OPAQUE | BITMAP_PERSISTENT); + if (!bitmap) { + warn_user("NoMemory", 0); + return; + } + thumbnail_create(content, bitmap, url); + } + entry->bitmap = bitmap; + + history_layout(history); +} + + +/** + * Update the thumbnail for the current entry. + * + * \param history opaque history structure, as returned by history_create() + * \param content content for current entry + */ + +void history_update(struct history *history, struct content *content) +{ + if (!history || !history->current || !history->current->bitmap) + return; + + thumbnail_create(content, history->current->bitmap, 0); +} + + +/** + * Free a history structure. + * + * \param history opaque history structure, as returned by history_create() + */ + +void history_destroy(struct history *history) +{ + if (!history) + return; + history_free_entry(history->start); + free(history); +} + + +/** + * Free an entry in the tree recursively. + */ + +void history_free_entry(struct history_entry *entry) +{ + if (!entry) + return; + history_free_entry(entry->forward); + history_free_entry(entry->next); + free(entry->url); + if (entry->frag_id) + free(entry->frag_id); + free(entry->title); + free(entry); +} + + +/** + * Go back in the history. + * + * \param bw browser window + * \param history history of the window + */ + +void history_back(struct browser_window *bw, struct history *history) +{ + if (!history || !history->current || !history->current->back) + return; + history_go(bw, history, history->current->back, false); +} + + +/** + * Go forward in the history. + * + * \param bw browser window + * \param history history of the window + */ + +void history_forward(struct browser_window *bw, struct history *history) +{ + if (!history || !history->current || !history->current->forward_pref) + return; + history_go(bw, history, history->current->forward_pref, false); +} + + +/** + * Check whether it is pssible to go back in the history. + * + * \param history history of the window + * \return true if the history can go back, false otherwise + */ + +bool history_back_available(struct history *history) +{ + return (history && history->current && history->current->back); +} + + +/** + * Check whether it is pssible to go forwards in the history. + * + * \param history history of the window + * \return true if the history can go forwards, false otherwise + */ + +bool history_forward_available(struct history *history) +{ + return (history && history->current && history->current->forward_pref); +} + + +/** + * Open a history entry in the specified browser window + * + * \param bw browser window + * \param history history containing entry + * \param entry entry to open + * \param new_window open entry in new window + */ + +void history_go(struct browser_window *bw, struct history *history, + struct history_entry *entry, bool new_window) +{ + char *url; + + if (entry->frag_id) { + url = malloc(strlen(entry->url) + strlen(entry->frag_id) + 5); + if (!url) { + warn_user("NoMemory", 0); + return; + } + sprintf(url, "%s#%s", entry->url, entry->frag_id); + } + else + url = entry->url; + + if (new_window) + browser_window_create(url, bw, 0); + else { + history->current = entry; + browser_window_go_post(bw, url, 0, 0, false, 0, false); + } + + if (entry->frag_id) + free(url); +} + + +/** + * Compute node positions. + * + * \param history history to layout + * + * Each node's x and y are filled in. + */ + +void history_layout(struct history *history) +{ + time_t t = time(0); + struct tm *tp = localtime(&t); + bool shuffle = tp->tm_mon == 3 && tp->tm_mday == 1; + + history->width = 0; + history->height = history_layout_subtree(history, history->start, + RIGHT_MARGIN / 2, BOTTOM_MARGIN / 2, shuffle); + if (shuffle) { + history->width = 600 + WIDTH; + history->height = 400 + HEIGHT; + } + history->width += RIGHT_MARGIN / 2; + history->height += BOTTOM_MARGIN / 2; +} + + +/** + * Recursively position a subtree. + * + * \param history history being laid out + * \param entry subtree to position + * \param x x position for entry + * \param y smallest available y + * \param shuffle shuffle layout + * \return greatest y used by subtree + */ + +int history_layout_subtree(struct history *history, + struct history_entry *entry, int x, int y, bool shuffle) +{ + struct history_entry *child; + int y1 = y; + + if (history->width < x + WIDTH) + history->width = x + WIDTH; + + if (!entry->forward) { + entry->x = x; + entry->y = y; + if (shuffle) { + entry->x = rand() % 600; + entry->y = rand() % 400; + } + return y + HEIGHT; + } + + /* layout child subtrees below each other */ + for (child = entry->forward; child; child = child->next) { + y1 = history_layout_subtree(history, child, + x + WIDTH + RIGHT_MARGIN, y1, shuffle); + if (child->next) + y1 += BOTTOM_MARGIN; + } + + /* place ourselves in the middle */ + entry->x = x; + entry->y = (y + y1) / 2 - HEIGHT / 2; + if (shuffle) { + entry->x = rand() % 600; + entry->y = rand() % 400; + } + + return y1; +} + + +/** + * Get the dimensions of a history. + * + * \param history history to measure + * \param width updated to width + * \param height updated to height + */ + +void history_size(struct history *history, int *width, int *height) +{ + *width = history->width; + *height = history->height; +} + + +/** + * Redraw a history. + * + * \param history history to render + * + * The current plotter is used. + */ + +bool history_redraw(struct history *history) +{ + if (!history->start) + return true; + return history_redraw_entry(history, history->start); +} + + +/** + * Recursively redraw a history_entry. + * + * \param history history containing the entry + * \param history_entry entry to render + */ + +bool history_redraw_entry(struct history *history, + struct history_entry *entry) +{ + size_t char_offset; + int actual_x; + struct history_entry *child; + colour c = entry == history->current ? 0x0000ff : 0x333333; + + if (!plot.bitmap(entry->x, entry->y, WIDTH, HEIGHT, entry->bitmap, + 0xffffff)) + return false; + if (!plot.rectangle(entry->x - 1, entry->y - 1, WIDTH + 1, HEIGHT + 1, + entry == history->current ? 2 : 1, c, false, false)) + return false; + + if (!nsfont_position_in_string(&css_base_style, entry->title, + strlen(entry->title), WIDTH, &char_offset, &actual_x)) + return false; + if (!plot.text(entry->x, entry->y + HEIGHT + 12, &css_base_style, + entry->title, char_offset, 0xffffff, c)) + return false; + + for (child = entry->forward; child; child = child->next) { + if (!plot.line(entry->x + WIDTH, entry->y + HEIGHT / 2, + child->x, child->y + HEIGHT / 2, 1, + 0x333333, false, false)) + return false; + if (!history_redraw_entry(history, child)) + return false; + } + + return true; +} + + +/** + * Handle a mouse click in a history. + * + * \param bw browser window containing history + * \param history history that was clicked in + * \param x click coordinate + * \param y click coordinate + * \param new_window open a new window instead of using bw + * \return true if action was taken, false if click was not on an entry + */ + +bool history_click(struct browser_window *bw, struct history *history, + int x, int y, bool new_window) +{ + struct history_entry *entry; + + entry = history_find_position(history->start, x, y); + if (!entry) + return false; + if (entry == history->current) + return false; + + history_go(bw, history, entry, new_window); + + return true; +} + + +/** + * Determine the URL of the entry at a position. + * + * \param history history to search + * \param x coordinate + * \param y coordinate + * \return URL, or 0 if no entry at (x, y) + */ + +const char *history_position_url(struct history *history, int x, int y) +{ + struct history_entry *entry; + + entry = history_find_position(history->start, x, y); + if (!entry) + return 0; + + return entry->url; +} + + +/** + * Find the history entry at a position. + * + * \param entry entry to search from + * \param x coordinate + * \param y coordinate + * \return an entry if found, 0 if none + */ + +struct history_entry *history_find_position(struct history_entry *entry, + int x, int y) +{ + struct history_entry *child; + struct history_entry *found; + + if (entry->x <= x && x <= entry->x + WIDTH && + entry->y <= y && y <= entry->y + HEIGHT) + return entry; + + for (child = entry->forward; child; child = child->next) { + found = history_find_position(child, x, y); + if (found) + return found; + } + + return 0; +} |