/* * 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 2004 James Bursa */ /** \file * Browser history tree and window (implementation). */ #include #include #include #include #include "oslib/colourtrans.h" #include "oslib/font.h" #include "oslib/wimp.h" #include "netsurf/riscos/options.h" #include "netsurf/riscos/gui.h" #include "netsurf/riscos/thumbnail.h" #include "netsurf/riscos/tinct.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/utils.h" #define SIZE 10 #define WIDTH 200 #define HEIGHT 152 #define MARGIN 32 #define FULL_WIDTH (WIDTH + MARGIN + MARGIN) #define FULL_HEIGHT (HEIGHT + MARGIN + MARGIN) #define SPRITE_SIZE (16 + 44 + ((WIDTH / 2 + 3) & ~3) * HEIGHT / 2) /** 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, y, width; osspriteop_area *sprite_area; /**< Thumbnail sprite area, 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; }; static struct browser_window *history_bw; static struct history *history_current = 0; /* Position of mouse in window */ static int mouse_x = 0, mouse_y = 0; wimp_w history_window; font_f history_font; static void history_free_entry(struct history_entry *entry); static void ro_gui_history_redraw_tree(struct history_entry *he, int x0, int y0); static struct history_entry * ro_gui_history_click_find(struct history_entry *he, int x, int y); static void history_go(struct browser_window *bw, struct history_entry *entry); /** * 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) { struct history_entry *entry; char *url; char *title; char *split; int width; osspriteop_area *area; // os_error *error; if (!history) return; /* allocate space */ entry = malloc(sizeof *entry); url = strdup(content->url); title = strdup(content->title ? content->title : url); if (!entry || !url || !title) { warn_user("NoMemory", 0); free(entry); free(url); free(title); return; } /* truncate title to available width */ font_scan_string(history_font, title, font_GIVEN_FONT | font_KERN, WIDTH * 400, 0x7fffffff, 0, 0, 0, &split, &width, 0, 0); if (title[split - title]) { title[split - title - 2] = 0x8c; /* ellipsis */ title[split - title - 1] = 0; } 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->width = width / 400; entry->sprite_area = 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; /* area = malloc(SPRITE_SIZE); if (!area) { LOG(("malloc failed")); return; } area->size = SPRITE_SIZE; area->sprite_count = 0; area->first = 16; area->used = 16; error = xosspriteop_create_sprite(osspriteop_NAME, area, "thumbnail", false, WIDTH / 2, HEIGHT / 2, os_MODE8BPP90X90); if (error) { LOG(("0x%x: %s", error->errnum, error->errmess)); return; } */ area = thumbnail_initialise(WIDTH / 2, HEIGHT / 2, (os_mode)0x301680b5); if (!area) { LOG(("Thumbnail initialisation failed.")); return; } thumbnail_create(content, area, (osspriteop_header *) (area + 1), WIDTH / 2, HEIGHT / 2); /* xosspriteop_save_sprite_file(osspriteop_NAME, area, "thumbnail");*/ entry->sprite_area = area; } /** * 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->sprite_area) return; thumbnail_create(content, history->current->sprite_area, (osspriteop_header *) (history->current->sprite_area + 1), WIDTH / 2, HEIGHT / 2); } /** * Free a history structure. * * \param history opaque history structure, as returned by history_create() */ void history_destroy(struct history *history) { if (!history) return; if (history_current == history) { wimp_close_window(history_window); history_current = 0; } 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->sprite_area); free(entry); } /** * Create history window. */ void ro_gui_history_init(void) { history_window = ro_gui_dialog_create("history"); history_font = font_find_font("Homerton.Medium", 112, 128, 0, 0, 0, 0); } /** * Free history window resources. */ void ro_gui_history_quit(void) { font_lose_font(history_font); } /** * Update resources folowing a mode change */ void ro_gui_history_mode_change(void) { font_lose_font(history_font); history_font = font_find_font("Homerton.Medium", 112, 128, 0, 0, 0, 0); } /** * Open history window. */ void ro_gui_history_open(struct browser_window *bw, struct history *history, int wx, int wy) { bool done = false; unsigned int i, j, max_y = 0; int x; int width, height; struct history_entry *row[SIZE], *row2[SIZE]; struct history_entry *he; os_box box = {0, 0, 0, 0}; wimp_window_state state; if (!history || !history->start) return; history_bw = bw; history_current = history; /* calculate layout */ for (i = 0; i != SIZE; i++) row[i] = row2[i] = 0; row[0] = history->start; history->start->x = 0; history->start->y = 0; for (x = 1; !done; x++) { for (i = 0; i != SIZE; i++) { if (row[i]) { for (j = i; j != SIZE && row2[j]; j++) ; if (j == SIZE) { if (row[i]->forward) row[i]->forward->x = -1; break; } for (he = row[i]->forward; he; he = he->next) { row2[j++] = he; if (j == SIZE) { if (he->next) he->next->x = -1; break; } } if (j == SIZE) break; } } done = true; for (i = 0; i != SIZE; i++) { row[i] = row2[i]; if (row[i]) { row[i]->x = x; row[i]->y = i; if (max_y < i) max_y = i; done = false; } row2[i] = 0; } } width = FULL_WIDTH * (x - 1); height = FULL_HEIGHT * (max_y + 1); box.x1 = width; box.y0 = -height; wimp_set_extent(history_window, &box); state.w = history_window; wimp_get_window_state(&state); state.visible.x0 = wx - width / 2; state.visible.y0 = wy - height / 2; state.visible.x1 = wx + width / 2; state.visible.y1 = wy + height / 2; state.next = wimp_TOP; wimp_open_window((wimp_open *) &state); } /** * Redraw history window. */ void ro_gui_history_redraw(wimp_draw *redraw) { osbool more; more = wimp_redraw_window(redraw); while (more) { ro_gui_history_redraw_tree(history_current->start, redraw->box.x0 - redraw->xscroll, redraw->box.y1 - redraw->yscroll); more = wimp_get_rectangle(redraw); } } /** * Redraw history tree recursively. */ void ro_gui_history_redraw_tree(struct history_entry *he, int x0, int y0) { struct history_entry *c; /* os_plot(os_MOVE_TO, x0 + he->x * FULL_WIDTH + MARGIN, y0 - he->y * FULL_HEIGHT - MARGIN); os_plot(os_PLOT_RECTANGLE | os_PLOT_BY, WIDTH, -HEIGHT);*/ if (he == history_current->current) colourtrans_set_gcol(os_COLOUR_RED, 0, os_ACTION_OVERWRITE, 0); else colourtrans_set_gcol(os_COLOUR_MID_DARK_GREY, 0, os_ACTION_OVERWRITE, 0); os_plot(os_MOVE_TO, x0 + he->x * FULL_WIDTH + MARGIN - 1, y0 - he->y * FULL_HEIGHT - MARGIN); os_plot(os_PLOT_SOLID | os_PLOT_BY, WIDTH + 1, 0); os_plot(os_PLOT_SOLID | os_PLOT_BY, 0, -HEIGHT - 1); os_plot(os_PLOT_SOLID | os_PLOT_BY, -WIDTH - 1, 0); os_plot(os_PLOT_SOLID | os_PLOT_BY, 0, HEIGHT + 1); if (he->sprite_area) { osspriteop_area *area = he->sprite_area; osspriteop_header *header = (osspriteop_header *)(area + 1); osspriteop_trans_tab *table; /* Because we're supporting people with OS3.1 we need to check if the sprite we have is a legacy 256 colour one */ if (header->mode == (os_mode)tinct_SPRITE_MODE) { /* We plot with no mask and no scaling as any EIG factors are handled internally by Tinct */ _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), (char *)(header), x0 + he->x * FULL_WIDTH + MARGIN, y0 - he->y * FULL_HEIGHT - FULL_HEIGHT + MARGIN, tinct_ERROR_DIFFUSE | tinct_BILINEAR_FILTER); } else { unsigned int size; os_factors factors; xcolourtrans_generate_table_for_sprite( area, (osspriteop_id)header, colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE, 0, colourtrans_GIVEN_SPRITE, 0, 0, &size); table = calloc(size, 1); if (!table) { LOG(("Insufficient memory for calloc")); warn_user("NoMemory", 0); } else { xcolourtrans_generate_table_for_sprite( area, (osspriteop_id)header, colourtrans_CURRENT_MODE, colourtrans_CURRENT_PALETTE, table, colourtrans_GIVEN_SPRITE, 0, 0, 0); factors.xmul = 1; factors.ymul = 1; factors.xdiv = 1; factors.ydiv = 1; xosspriteop_put_sprite_scaled(osspriteop_PTR, area, (osspriteop_id)header, x0 + he->x * FULL_WIDTH + MARGIN, y0 - he->y * FULL_HEIGHT - FULL_HEIGHT + MARGIN, osspriteop_USE_MASK | osspriteop_USE_PALETTE, &factors, table); free(table); } } } if (he == history_current->current) wimp_set_font_colours(wimp_COLOUR_WHITE, wimp_COLOUR_RED); else wimp_set_font_colours(wimp_COLOUR_WHITE, wimp_COLOUR_BLACK); xfont_paint(history_font, he->title, font_OS_UNITS | font_GIVEN_FONT | font_KERN, x0 + he->x * FULL_WIDTH + (FULL_WIDTH - he->width) / 2, y0 - he->y * FULL_HEIGHT - HEIGHT - MARGIN - 24, NULL, NULL, 0); colourtrans_set_gcol(os_COLOUR_MID_DARK_GREY, 0, os_ACTION_OVERWRITE, 0); for (c = he->forward; c; c = c->next) { if (c->x == -1) continue; os_plot(os_MOVE_TO, x0 + c->x * FULL_WIDTH - MARGIN, y0 - he->y * FULL_HEIGHT - FULL_HEIGHT / 2); os_plot(os_PLOT_SOLID | os_PLOT_TO, x0 + c->x * FULL_WIDTH + MARGIN, y0 - c->y * FULL_HEIGHT - FULL_HEIGHT / 2); ro_gui_history_redraw_tree(c, x0, y0); } } /** * Handle mouse movements over the history window. */ void ro_gui_history_mouse_at(wimp_pointer *pointer) { int x, y; long width; struct history_entry *he; wimp_window_state state; wimp_icon_state ic; os_box box = {0, 0, 0, 0}; /* If the mouse hasn't moved, or if we don't want tooltips, exit */ if ((mouse_x == pointer->pos.x && mouse_y == pointer->pos.y) || !option_history_tooltip) return; /* Update mouse position */ mouse_x = pointer->pos.x; mouse_y = pointer->pos.y; /* Find history tree entry under mouse */ state.w = history_window; wimp_get_window_state(&state); x = (pointer->pos.x - (state.visible.x0 - state.xscroll)) / FULL_WIDTH; y = -(pointer->pos.y - (state.visible.y1 - state.yscroll)) / FULL_HEIGHT; he = ro_gui_history_click_find(history_current->start, x, y); if (he) { /* get width of string */ xwimptextop_string_width(he->url, strlen(he->url) > 256 ? 256 : strlen(he->url), (int*)&width); ro_gui_set_icon_string(dialog_tooltip, 0, he->url); /* resize icon appropriately */ ic.w = dialog_tooltip; ic.i = 0; wimp_get_icon_state(&ic); wimp_resize_icon(dialog_tooltip, 0, ic.icon.extent.x0, ic.icon.extent.y0, width + 16, ic.icon.extent.y1); state.w = dialog_tooltip; wimp_get_window_state(&state); /* update window extent */ box.x1 = width + 16; box.y0 = -36; xwimp_set_extent(dialog_tooltip, &box); /* set visible area */ state.visible.x0 = pointer->pos.x + 24; state.visible.y0 = pointer->pos.y - 22 - 36; state.visible.x1 = pointer->pos.x + 24 + width + 16; state.visible.y1 = pointer->pos.y - 22; state.next = wimp_TOP; /* open window */ wimp_open_window((wimp_open *) &state); } else { /* not over a tree entry => close tooltip window. */ wimp_close_window(dialog_tooltip); } } /** * Handle mouse clicks in the history window. */ void ro_gui_history_click(wimp_pointer *pointer) { int x, y; struct history_entry *he; wimp_window_state state; state.w = history_window; wimp_get_window_state(&state); x = (pointer->pos.x - (state.visible.x0 - state.xscroll)) / FULL_WIDTH; y = -(pointer->pos.y - (state.visible.y1 - state.yscroll)) / FULL_HEIGHT; LOG(("x = %i, y = %i", x, y)); he = ro_gui_history_click_find(history_current->start, x, y); if (he) { history_current->current = he; wimp_close_window(history_window); history_current = 0; history_go(history_bw, he); } } /** * Search for an entry with the specified coordinates. */ struct history_entry * ro_gui_history_click_find(struct history_entry *he, int x, int y) { struct history_entry *c, *d; if (he->x == x && he->y == y) return he; for (c = he->forward; c; c = c->next) { d = ro_gui_history_click_find(c, x, y); if (d) return d; } return 0; } /** * 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->current = history->current->back; history_go(bw, history->current); } /** * 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->current = history->current->forward_pref; history_go(bw, history->current); } /** * 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 entry entry to open */ void history_go(struct browser_window *bw, struct history_entry *entry) { char *url; if (entry->frag_id) { url = calloc(strlen(entry->url) + strlen(entry->frag_id) + 5, sizeof(char)); if (!url) { warn_user("NoMemory", 0); return; } sprintf(url, "%s#%s", entry->url, entry->frag_id); } else url = entry->url; browser_window_go_post(bw, url, 0, 0, false, 0, false); if (entry->frag_id) free(url); }