From 5a72700817565b139e9576738d5b1ec23e23e69e Mon Sep 17 00:00:00 2001 From: James Bursa Date: Sat, 25 Mar 2006 20:30:35 +0000 Subject: [project @ 2006-03-25 20:30:35 by bursa] Split local history into portable and RISC OS specific code. Improve layout of history tree. svn path=/import/netsurf/; revision=2164 --- desktop/browser.c | 3 + desktop/browser.h | 16 +- desktop/history_core.c | 545 +++++++++++++++++++++++++++++++++++++++++++++++++ desktop/history_core.h | 36 ++++ 4 files changed, 588 insertions(+), 12 deletions(-) create mode 100644 desktop/history_core.c create mode 100644 desktop/history_core.h (limited to 'desktop') diff --git a/desktop/browser.c b/desktop/browser.c index b709ba289..031089b9d 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -29,6 +29,7 @@ #include "netsurf/desktop/401login.h" #endif #include "netsurf/desktop/browser.h" +#include "netsurf/desktop/history_core.h" #include "netsurf/desktop/gui.h" #include "netsurf/desktop/options.h" #include "netsurf/desktop/selection.h" @@ -173,6 +174,8 @@ void browser_window_go_post(struct browser_window *bw, const char *url, char url_buf[256]; LOG(("bw %p, url %s", bw, url)); + assert(bw); + assert(url); res = url_normalize(url, &url2); if (res != URL_FUNC_OK) { diff --git a/desktop/browser.h b/desktop/browser.h index 67da0abea..642512592 100644 --- a/desktop/browser.h +++ b/desktop/browser.h @@ -3,7 +3,7 @@ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2003 Phil Mellor - * Copyright 2004 James Bursa + * Copyright 2006 James Bursa */ /** \file @@ -150,22 +150,14 @@ void browser_window_redraw_rect(struct browser_window *bw, int x, int y, /* In platform specific hotlist.c. */ void hotlist_visited(struct content *content); -/* In platform specific history.c. */ -struct history *history_create(void); -void history_add(struct history *history, struct content *content, - char *frag_id); -void history_update(struct history *history, struct content *content); -void history_destroy(struct history *history); -void history_back(struct browser_window *bw, struct history *history); -void history_forward(struct browser_window *bw, struct history *history); -bool history_back_available(struct history *history); -bool history_forward_available(struct history *history); - /* In platform specific global_history.c. */ void global_history_add(struct url_content *data); void global_history_add_recent(const char *url); char **global_history_get_recent(int *count); +/* In platform specific thumbnail.c. */ +bool thumbnail_create(struct content *content, struct bitmap *bitmap, + const char *url); /* In platform specific schedule.c. */ void schedule(int t, void (*callback)(void *p), void *p); 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 + * Copyright 2005 Richard Wilson + */ + +/** \file + * Browser history tree (implementation). + */ + +#include +#include +#include +#include +#include +#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; +} diff --git a/desktop/history_core.h b/desktop/history_core.h new file mode 100644 index 000000000..4889ad524 --- /dev/null +++ b/desktop/history_core.h @@ -0,0 +1,36 @@ +/* + * 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 + */ + +/** \file + * Browser history tree (interface). + */ + +#ifndef _NETSURF_DESKTOP_HISTORY_H_ +#define _NETSURF_DESKTOP_HISTORY_H_ + +#include + +struct content; +struct history; +struct browser_window; + +struct history *history_create(void); +void history_add(struct history *history, struct content *content, + char *frag_id); +void history_update(struct history *history, struct content *content); +void history_destroy(struct history *history); +void history_back(struct browser_window *bw, struct history *history); +void history_forward(struct browser_window *bw, struct history *history); +bool history_back_available(struct history *history); +bool history_forward_available(struct history *history); +void history_size(struct history *history, int *width, int *height); +bool history_redraw(struct history *history); +bool history_click(struct browser_window *bw, struct history *history, + int x, int y, bool new_window); +const char *history_position_url(struct history *history, int x, int y); + +#endif -- cgit v1.2.3