/* * 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 Richard Wilson */ /** \file * Hotlist (implementation). */ #include #include #include #include #include "oslib/colourtrans.h" #include "oslib/osfile.h" #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "netsurf/content/content.h" #include "netsurf/riscos/gui.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" #include "netsurf/utils/utils.h" #define HOTLIST_EXPAND 0 #define HOTLIST_COLLAPSE 1 #define HOTLIST_ENTRY 2 #define HOTLIST_LINE 3 #define HOTLIST_TLINE 4 #define HOTLIST_BLINE 5 #define HOTLIST_TEXT_BUFFER 256 #define HOTLIST_LEAF_INSET 32 #define HOTLIST_ICON_WIDTH 36 #define HOTLIST_LINE_HEIGHT 44 #define HOTLIST_TEXT_PADDING 16 #define HOTLIST_LOAD_BUFFER 1024 struct hotlist_entry { /** The next hotlist entry at this level, or NULL for no more */ struct hotlist_entry *next_entry; /** The child hotlist entry (NULL for no children). The children value must be set for this value to take effect. */ struct hotlist_entry *child_entry; /** The number of children (-1 for non-folders, >=0 for folders) */ int children; /** The title of the hotlist entry/folder */ char *title; /** The URL of the hotlist entry (NULL for folders) */ char *url; /** Whether this entry is expanded */ bool expanded; /** Whether this entry is selected */ bool selected; /** The content filetype (not for folders) */ int filetype; /** The number of visits */ int visits; /** Add/last visit dates */ time_t add_date; time_t last_date; /** Position on last reformat (relative to window origin) */ int x0; int y0; int width; int height; /** Cached values */ int collapsed_width; int expanded_width; /** The width of the various lines sub-text */ int widths[4]; }; /* A basic window for the toolbar and status */ static wimp_window hotlist_window_definition = { {0, 0, 600, 800}, 0, 0, wimp_TOP, wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_SIZE_ICON |wimp_WINDOW_VSCROLL, wimp_COLOUR_BLACK, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_VERY_LIGHT_GREY, wimp_COLOUR_DARK_GREY, wimp_COLOUR_MID_LIGHT_GREY, wimp_COLOUR_CREAM, 0, {0, -800, 16384, 0}, wimp_ICON_TEXT | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED, (wimp_BUTTON_DOUBLE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT), wimpspriteop_AREA, 1, 1, {"Hotlist"}, 0 }; /* An icon to plot text with */ static wimp_icon text_icon; static wimp_icon sprite_icon; static char null_text_string[] = "\0"; /* Temporary workspace for plotting */ static char icon_name[12]; static char extended_text[HOTLIST_TEXT_BUFFER]; /* Whether a reformat is pending */ static bool reformat_pending = false; static int max_width = 0; static int max_height = 0; /* The hotlist window and plot origins */ wimp_w hotlist_window; static int origin_x, origin_y; /* The current redraw rectangle */ static int clip_x0, clip_y0, clip_x1, clip_y1; /* The root entry */ static struct hotlist_entry root; /* The sprite ids for far faster plotting */ static osspriteop_id sprite[6]; /* Pixel translation tables */ static osspriteop_trans_tab *pixel_table; /* The drag buttons */ wimp_mouse_state drag_buttons; /* Whether the current selection was from a menu click */ bool menu_selection = false; /* Hotlist loading buffer */ char *load_buf; static bool ro_gui_hotlist_load(void); static bool ro_gui_hotlist_save_entry(FILE *fp, struct hotlist_entry *entry); static bool ro_gui_hotlist_load_entry(FILE *fp, struct hotlist_entry *entry); static void ro_gui_hotlist_link_entry(struct hotlist_entry *parent, struct hotlist_entry *entry); static void ro_gui_hotlist_visited_update(struct content *content, struct hotlist_entry *entry); static int ro_gui_hotlist_redraw_tree(struct hotlist_entry *entry, int level, int x0, int y0); static int ro_gui_hotlist_redraw_item(struct hotlist_entry *entry, int level, int x0, int y0); static struct hotlist_entry *ro_gui_hotlist_create(const char *title, const char *url, int filetype, struct hotlist_entry *folder); static void ro_gui_hotlist_update_entry_size(struct hotlist_entry *entry); static struct hotlist_entry *ro_gui_hotlist_find_entry(int x, int y, struct hotlist_entry *entry); static int ro_gui_hotlist_selection_state(struct hotlist_entry *entry, bool selected, bool redraw); static void ro_gui_hotlist_selection_drag(struct hotlist_entry *entry, int x0, int y0, int x1, int y1, bool toggle, bool redraw); static int ro_gui_hotlist_selection_count(struct hotlist_entry *entry); static void ro_gui_hotlist_update_expansion(struct hotlist_entry *entry, bool only_selected, bool folders, bool links, bool expand, bool contract); static void ro_gui_hotlist_launch_selection(struct hotlist_entry *entry); static char *last_visit_to_string(time_t last_visit); void ro_gui_hotlist_init(void) { os_error *error; /* Set the initial root options */ root.next_entry = NULL; root.child_entry = NULL; root.children = 0; root.expanded = true; /* Load the hotlist */ if (!ro_gui_hotlist_load()) { // return; } /* Get our sprite ids for faster plotting. This could be done in a far more elegant manner, but it's late and my girlfriend will kill me if I don't go to bed soon. Sorry. */ error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_expand", (osspriteop_header **)&sprite[HOTLIST_EXPAND]); if (!error) error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_collapse", (osspriteop_header **)&sprite[HOTLIST_COLLAPSE]); if (!error) error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_entry", (osspriteop_header **)&sprite[HOTLIST_ENTRY]); if (!error) error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_line", (osspriteop_header **)&sprite[HOTLIST_LINE]); if (!error) error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_halflinet", (osspriteop_header **)&sprite[HOTLIST_TLINE]); if (!error) error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, (osspriteop_id)"tr_halflineb", (osspriteop_header **)&sprite[HOTLIST_BLINE]); if (error) { warn_user("MiscError", error->errmess); return; } /* Update our text icon */ text_icon.data.indirected_text.validation = null_text_string; text_icon.data.indirected_text.size = 256; sprite_icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); sprite_icon.data.indirected_sprite.area = wimpspriteop_AREA; sprite_icon.data.indirected_text.size = 12; /* Create our window */ error = xwimp_create_window(&hotlist_window_definition, &hotlist_window); if (error) { warn_user("WimpError", error->errmess); return; } } /** * Shows the hotlist window. */ void ro_gui_hotlist_show(void) { os_error *error; int screen_width, screen_height; wimp_window_state state; int dimension; int scroll_width; /* We may have failed to initialise */ if (!hotlist_window) return; /* Get the window state */ state.w = hotlist_window; error = xwimp_get_window_state(&state); if (error) { warn_user("WimpError", error->errmess); return; } /* If we're open we jump to the top of the stack, if not then we open in the centre of the screen. */ if (!(state.flags & wimp_WINDOW_OPEN)) { /* Clear the selection/expansion states */ ro_gui_hotlist_update_expansion(root.child_entry, false, true, true, false, true); ro_gui_hotlist_selection_state(root.child_entry, false, false); /* Get the current screen size */ ro_gui_screen_size(&screen_width, &screen_height); /* Move to the centre */ dimension = state.visible.x1 - state.visible.x0; scroll_width = ro_get_vscroll_width(hotlist_window); state.visible.x0 = (screen_width - (dimension + scroll_width)) / 2; state.visible.x1 = state.visible.x0 + dimension; dimension = state.visible.y1 - state.visible.y0; state.visible.y0 = (screen_height - dimension) / 2; state.visible.y1 = state.visible.y0 + dimension; } /* Open the window at the top of the stack */ state.next = wimp_TOP; error = xwimp_open_window((wimp_open*)&state); if (error) { warn_user("WimpError", error->errmess); return; } } bool ro_gui_hotlist_load(void) { FILE *fp; fileswitch_object_type obj_type = 0; struct hotlist_entry *netsurf; struct hotlist_entry *entry; bool success = true; bool found = false; /* Check if we have an initial hotlist. OS_File does funny things relating to errors, so we use the object type to determine success */ xosfile_read_stamped_no_path(".WWW.NetSurf.Hotlist", &obj_type, (bits)0, (bits)0, 0, (fileswitch_attr)0, (bits)0); if (obj_type != 0) { /* Open our file */ fp = fopen(".WWW.NetSurf.Hotlist", "r"); if (!fp) { warn_user("HotlistLoadError", 0); return false; } /* Get some memory to work with */ load_buf = malloc(HOTLIST_LOAD_BUFFER); if (!load_buf) { warn_user("HotlistLoadError", 0); fclose(fp); return false; } /* Check for the opening to vaguely validate our file */ if ((fgets(load_buf, HOTLIST_LOAD_BUFFER, fp) == NULL) || (strncmp("", load_buf, 6) != 0)) { warn_user("HotlistLoadError", 0); free(load_buf); fclose(fp); return false; } /* Keep reading until we get to a
    */ while (!found && (fgets(load_buf, HOTLIST_LOAD_BUFFER, fp))) { if (strncmp("
      ", load_buf, 4) == 0) found = true; } /* Start our recursive load */ if (found) success = ro_gui_hotlist_load_entry(fp, &root); /* Tell the user if we had any problems */ if (!success) { warn_user("HotlistLoadError", 0); } /* Close our file and return */ free(load_buf); fclose(fp); return success; } else { /* Create a folder */ netsurf = ro_gui_hotlist_create("NetSurf", NULL, 0, &root); /* Add some content */ entry = ro_gui_hotlist_create("NetSurf homepage", "http://netsurf.sf.net", 0xfaf, netsurf); entry->add_date = (time_t)-1; entry = ro_gui_hotlist_create("NetSurf test builds", "http://netsurf.strcprstskrzkrk.co.uk", 0xfaf, netsurf); entry->add_date = (time_t)-1; /* We succeeded */ return true; } } void ro_gui_hotlist_save(void) { FILE *fp; /* Don't save if we didn't load */ if (!hotlist_window) return; /* Ensure we have a directory to save to later. */ xosfile_create_dir(".WWW", 0); xosfile_create_dir(".WWW.NetSurf", 0); /* Open our file */ fp = fopen(".WWW.NetSurf.Hotlist", "w"); if (!fp) { warn_user("HotlistSaveError", 0); return; } /* HTML header */ fprintf(fp, "\n\nHotlist\n\n"); /* Start our recursive save */ if (!ro_gui_hotlist_save_entry(fp, root.child_entry)) { warn_user("HotlistSaveError", 0); } /* HTML footer */ fprintf(fp, "\n\n"); /* Close our file */ fclose(fp); /* Set the filetype to HTML */ xosfile_set_type(".WWW.NetSurf.Hotlist", 0xfaf); } bool ro_gui_hotlist_save_entry(FILE *fp, struct hotlist_entry *entry) { if (!entry) return true; /* We're starting a new child */ fprintf(fp, "
        \n"); /* Work through the entries */ while (entry) { /* Save this entry */ if (entry->url) { fprintf(fp, "
      • %s\n", entry->url, entry->title); } else { fprintf(fp, "
      • %s\n", entry->title); } fprintf(fp, "\n", entry->title); if (entry->url) { fprintf(fp, "\n", entry->url); fprintf(fp, "\n", entry->filetype); } if (entry->add_date != -1) fprintf(fp, "\n", (int)entry->add_date); if (entry->last_date != -1) fprintf(fp, "\n", (int)entry->last_date); if (entry->visits != 0) fprintf(fp, "\n", entry->visits); /* Continue onwards */ if (entry->child_entry) { ro_gui_hotlist_save_entry(fp, entry->child_entry); } entry = entry->next_entry; } /* We're starting a new child */ fprintf(fp, "
      \n"); return true; } bool ro_gui_hotlist_load_entry(FILE *fp, struct hotlist_entry *entry) { struct hotlist_entry *last_entry = NULL; char *title = NULL; char *url = NULL; int add_date = -1; int last_date = -1; int visits = 0; int filetype = 0; int val_length; /* Check we can add to something */ if (entry == NULL) return false; /* Keep reading until we get to a
        */ while (fgets(load_buf, HOTLIST_LOAD_BUFFER, fp)) { /* Check if we should commit what we have */ if ((strncmp("
      • ", load_buf, 4) == 0) || (strncmp("
      ", load_buf, 5) == 0) || (strncmp("
        ", load_buf, 4) == 0)) { if (title != NULL) { /* Add the entry */ last_entry = ro_gui_hotlist_create(title, url, filetype, entry); last_entry->add_date = add_date; if (last_entry->url) { last_entry->last_date = last_date; last_entry->visits = visits; last_entry->filetype = filetype; } /* Reset for the next entry */ free(title); title = NULL; if (url) { free(url); url = NULL; } add_date = -1; last_date = -1; visits = 0; filetype = 0; } } /* Check if we've reached the end of our run */ if (strncmp("
      ", load_buf, 5) == 0) return true; /* Check for some data */ if (strncmp("