/** Visited URL history managment - NOT dialog_goto_url() history! * @file */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "elinks.h" #include "cache/cache.h" #include "config/options.h" #include "dialogs/status.h" #include "network/connection.h" #include "protocol/uri.h" #include "session/history.h" #include "session/location.h" #include "session/session.h" #include "session/task.h" #include "util/memory.h" #include "util/string.h" #include "viewer/text/view.h" #include "viewer/text/vs.h" static inline void free_history(LIST_OF(struct location) *history) { while (!list_empty(*history)) { struct location *loc = (struct location *)history->next; del_from_list(loc); destroy_location(loc); } } /** @relates ses_history */ void create_history(struct ses_history *history) { init_list(history->history); history->current = NULL; } /** @relates ses_history */ void destroy_history(struct ses_history *history) { free_history(&history->history); history->current = NULL; } /** @relates ses_history */ void clean_unhistory(struct ses_history *history) { if (!history->current) return; while (list_has_next(history->history, history->current)) { struct location *loc = history->current->next; del_from_list(loc); destroy_location(loc); } } /** @relates ses_history */ void add_to_history(struct ses_history *history, struct location *loc) { if (!history->current) { add_to_list(history->history, loc); } else { add_at_pos(history->current, loc); } history->current = loc; } /** @relates ses_history */ void del_from_history(struct ses_history *history, struct location *loc) { if (history->current == loc) history->current = loc->prev; if (history->current == (struct location *) &history->history) history->current = loc->next; if (history->current == (struct location *) &history->history) history->current = NULL; del_from_list(loc); } void ses_history_move(struct session *ses) { struct location *loc; /* Prepare. */ free_files(ses); if (get_opt_bool("document.browse.search.reset", NULL)) { mem_free_set(&ses->search_word, NULL); } /* Does it make sense? */ if (!have_location(ses) || !ses->task.target.location) return; if (ses->task.target.location == (struct location *) &ses->history.history) return; /* Move. */ ses->history.current = ses->task.target.location; loc = cur_loc(ses); /* There can be only one ... */ if (compare_uri(loc->vs.uri, ses->loading_uri, 0)) return; /* Remake that location. */ del_from_history(&ses->history, loc); destroy_location(loc); ses_forward(ses, 0); /* Maybe trash the unhistory. */ if (get_opt_bool("document.history.keep_unhistory", ses)) clean_unhistory(&ses->history); } void go_history(struct session *ses, struct location *loc) { ses->reloadlevel = CACHE_MODE_NORMAL; if (ses->task.type) { abort_loading(ses, 0); print_screen_status(ses); reload(ses, CACHE_MODE_NORMAL); return; } if (!have_location(ses) || loc == (struct location *) &ses->history.history) { /* There's no history, at most only the current location. */ return; } abort_loading(ses, 0); set_session_referrer(ses, NULL); ses_goto(ses, loc->vs.uri, NULL, loc, CACHE_MODE_ALWAYS, TASK_HISTORY, 0); } int go_history_by_n(struct session *ses, int n) { struct location *loc = cur_loc(ses); if (!loc) return -1; if (n > 0) { while (n-- && list_has_next(ses->history.history, loc)) loc = loc->next; } else { while (n++ && list_has_prev(ses->history.history, loc)) loc = loc->prev; if (n == 0 && ! list_has_prev(ses->history.history, loc)) return -1; } go_history(ses, loc); return 0; } /** Go backward in the history. See go_history() description regarding * unpredictable effects on cur_loc() by this function. */ void go_back(struct session *ses) { go_history_by_n(ses, -1); } /** Go forward in the history. See go_history() description regarding * unpredictable effects on cur_loc() by this function. */ void go_unback(struct session *ses) { go_history_by_n(ses, 1); }