From 2c969f455df257b88903a93672211fc4fec2fcec Mon Sep 17 00:00:00 2001 From: Andrew Hodgkinson <ahodgkin@gitlab.riscosopen.org> Date: Wed, 27 Aug 1997 09:05:03 +0000 Subject: [PATCH] Tidied up Hotlist source a bit --- c/Hotlist | 3937 +++++++++++++++++++++++++++++++++++++++++++++++++++++ h/Hotlist | 204 +++ 2 files changed, 4141 insertions(+) create mode 100644 c/Hotlist create mode 100644 h/Hotlist diff --git a/c/Hotlist b/c/Hotlist new file mode 100644 index 0000000..205366a --- /dev/null +++ b/c/Hotlist @@ -0,0 +1,3937 @@ +/* Copyright 1997 Acorn Computers Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/***************************************************/ +/* File : Hotlist.c */ +/* */ +/* Purpose: Managing a hotlist in the browser. */ +/* */ +/* Author : D.T.A.Brown */ +/* */ +/* History: 06-Aug-97: Created. */ +/* 22-Aug-97: (ADH/DTAB) Integrated into */ +/* main browser code. */ +/***************************************************/ + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "swis.h" + +#include "toolbox.h" +#include "wimp.h" +#include "wimplib.h" +#include "menu.h" +#include "event.h" +#include "gadgets.h" + +#include "svcprint.h" +#include "Global.h" +#include "FromROSLib.h" +#include "Utils.h" + +#include "FetchPage.h" +#include "Save.h" +#include "Toolbars.h" +#include "Windows.h" + +#include "Hotlist.h" + +/* Local definitions */ + +#define HotlistWrite(fn) {written = (fn); if (written < 0) return _kernel_last_oserror();} + +/* Local statics */ + +static hotlist_item * hotlist_root = NULL; /* Pointer to the hotlist root directory item */ +static int hotlist_windowid; /* Object ID of the hotlist window */ + +static hotlist_item * hotlist_newitem; /* When ever a new item is created this points to it */ + +static int menu_itemno = 0; /* Item over which menu was pressed */ + +static hotlist_item * selected_parent = NULL; /* The parent directory of all selected items */ +static int number_selected = 0; /* The number of items selected */ +static unsigned int last_selected_item = 0xffffffff; /* The last item which was selected */ + +static int hotlist_dragging = 0; /* Holds a set of values for different dragging cases (see Hotlist.h) */ +static unsigned int highlighted_itemno; /* During dragging, the number of a directory item using the '...+' sprite */ +static hotlist_item * hotlist_current_highlighted = NULL; /* To save scanning lists, pointer to the highlighted item */ + + + +static unsigned int hotlist_bbar_size = 0; /* Height in OS units of the button bar */ +static unsigned int alter_new; /* Remembers if the edit dialogue is Alter or New; see */ + /* HOTLIST_MENUSECTION_NEW and HOTLIST_MENUSECTION_ALTER */ + +static unsigned int item_number; /* Current item being considered for drawing */ + +/* Event handler prototypes */ + +static _kernel_oserror * hotlist_selection_box_start(void); + +static WimpEventHandler hotlist_redraw_handler; +static WimpEventHandler hotlist_mouse_click_handler; +static WimpEventHandler hotlist_drag_completed_handler; +static WimpEventHandler hotlist_null_handler; +static WimpEventHandler hotlist_null_drag_select_handler; + +static ToolboxEventHandler hotlist_menuopen_handler; +static ToolboxEventHandler hotlist_menuclose_handler; +static ToolboxEventHandler hotlist_menu_selectall_handler; +static ToolboxEventHandler hotlist_menu_clearselect_handler; +static ToolboxEventHandler hotlist_menu_openall_handler; +static ToolboxEventHandler hotlist_menu_closeall_handler; +static ToolboxEventHandler hotlist_menu_delete_handler; +static ToolboxEventHandler hotlist_show_editurl_handler; +static ToolboxEventHandler hotlist_show_rendirectory_handler; +static ToolboxEventHandler hotlist_show_newurl_handler; +static ToolboxEventHandler hotlist_show_newdirectory_handler; +static ToolboxEventHandler hotlist_newedit_url_handler; +static ToolboxEventHandler hotlist_newren_directory_handler; +static ToolboxEventHandler hotlist_reset_url_handler; +static ToolboxEventHandler hotlist_reset_directory_handler; + +/* Debug functions */ + +#ifdef TRACE + + static void hotlist_display_item(hotlist_item * item); + static void hotlist_display_tree(hotlist_item * list, int indent); + +#endif + +/* Miscellaneous function prototypes */ + +static _kernel_oserror * hotlist_get_entry_sizes (unsigned int * item_height, unsigned int * item_dir_width, unsigned int * item_url_width); +static void hotlist_scan_for_subdirectories (hotlist_item * item); + +static hotlist_item * _hotlist_find_item (hotlist_item * list, unsigned int item_no); +static hotlist_item * hotlist_find_item (hotlist_item * list, unsigned int item_no); +static int _hotlist_find_no_from_item (hotlist_item * list, hotlist_item * item); +static int hotlist_find_no_from_item (hotlist_item * item); +static hotlist_item * _hotlist_find_selected_item (hotlist_item * list); +static hotlist_item * hotlist_find_selected_item (void); +static void _hotlist_count_displayed_items (hotlist_item * list); +static unsigned int hotlist_count_displayed_items (hotlist_item * list); +static void _hotlist_draw (hotlist_item * list, unsigned int first_item, unsigned int last_item, unsigned int indent); +static void hotlist_draw (hotlist_item * list, unsigned int first_item, unsigned int last_item); +static void _hotlist_get_max_width (hotlist_item * list, unsigned int indent); +static void _hotlist_redraw_now (hotlist_item * list); +static void hotlist_redraw_now (void); +static unsigned int hotlist_get_max_width (hotlist_item * list); +static void hotlist_get_shape (unsigned int * xmin, unsigned int * xmax, hotlist_item * item); +static void hotlist_directory_open_close (hotlist_item * item, unsigned int itemno); +static void hotlist_redraw_items (unsigned int firstitem, unsigned int lastitem); +static void hotlist_clear_selection (void); +static void hotlist_launch_url (hotlist_item * item); +static _kernel_oserror * hotlist_process_click_on_item (unsigned int itemno, hotlist_item * item, int buttons, int x, int y); +static _kernel_oserror * hotlist_process_click (int x, int y, int buttons); +static void hotlist_window_preopen (hl_opentype type); +static void hotlist_setmenu_details (ObjectId menuid); +static _kernel_oserror * hotlist_save_entries (FILE * fileptr, hotlist_item * list); +static void hotlist_lower_tags (char * string); +static _kernel_oserror * hotlist_load_directory (FILE * fileptr, hotlist_item * target); +static void hotlist_drag_renderer (hotlist_item * item, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width); +static void hotlist_start_drag (void); + +/* List manupulation */ + +static void hotlist_unlink (hotlist_item * item); +static _kernel_oserror * hotlist_link (hotlist_item * item, hotlist_item * target, unsigned int position); +static _kernel_oserror * hotlist_new_directory (hotlist_item * parent, char * directory_name, unsigned int position, hotlist_item ** new); +static _kernel_oserror * hotlist_new_url (hotlist_item * parent, unsigned int position, char * url_description, char * url); +static void hotlist_delete_item (hotlist_item * item); +static _kernel_oserror * hotlist_move_item (hotlist_item * source, hotlist_item * target, unsigned int position); +static _kernel_oserror * hotlist_copy_item (hotlist_item * source, hotlist_item * target, unsigned int position, hotlist_item ** new_item); +static int hotlist_set_flags (hotlist_item * list, hotlist_type type, unsigned int flags); +static int hotlist_clear_flags (hotlist_item * list, hotlist_type type, unsigned int flags); + +#ifdef TRACE + + /*************************************************/ + /* hotlist_display_item() */ + /* */ + /* This function display the data held by a */ + /* single hotlist_item */ + /* */ + /* Parameters: Pointer to a hotlist_item */ + /*************************************************/ + + static void hotlist_display_item(hotlist_item * item) + { + Printf("\nhotlist_display_item for %p\n",item); + Printf("--------------------\n"); + Printf("type = %d\n", item->type); + Printf("name = %s\n", item->name); + Printf("flags = %d\n", item->flags); + Printf("parent = %p\n", item->parent); + Printf("previous = %p\n", item->previous); + Printf("next = %p\n", item->next); + Printf("data.generic_data = %p ", item->data.generic_data); + + switch(item->type) + { + case hl_url: + { + Printf("URL(%s)\n", item->data.url); + } + break; + + case hl_directory: + { + Printf("sub directory pointer\n"); + } + break; + + default: + { + Printf("\n"); + } + break; + } + } + + /*************************************************/ + /* hotlist_display_tree() */ + /* */ + /* Recursivly display the hotlist tree starting */ + /* from the passed item */ + /* */ + /* Parameters: Pointer to a hotlist_item */ + /* value to start indent at */ + /* recommended 0 */ + /*************************************************/ + + static void hotlist_display_tree(hotlist_item * list, int indent) + { + int count; + + Printf("\nhotlist_display_tree for %p, indent %d\n", list, indent); + Printf("--------------------\n\n"); + + while (list) + { + for (count = 0; count < indent; count++) Printf("| "); + + switch (list->type) + { + case hl_url: + { + Printf("%s:URL(%s)\n", list->name, list->data.url); + } + break; + + case hl_directory: + { + Printf("%s:DIRECTORY", list->name); + } + if (list->flags & HOTLIST_D_HAS_SUBDIRECTORY) Printf(" (Has sub directory)"); + + if (list->flags & HOTLIST_D_IS_OPEN) + { + Printf(" (Open)\n"); + hotlist_display_tree(list->data.directory_content, indent + 1); + } + else + { + Printf(" (Closed)\n"); + hotlist_display_tree(list->data.directory_content, indent + 1); + } + break; + + default: + { + Printf("%s:UNRECOGNISED TYPE\n", list->name); + } + break; + } + list = list->next; + } + } + +#endif + +/*************************************************/ +/* hotlist_get_entry_sizes() */ +/* */ +/* This function reads the size of the sprites */ +/* to be used by the hotlist and from them */ +/* determines the size of the hotlist entries. */ +/* */ +/* Parameters: Pointer to an int, in which the */ +/* height of an item is placed in */ +/* OS units; */ +/* */ +/* Pointer to an int, in which the */ +/* minimum width of a directory item */ +/* is returned, in OS units; */ +/* */ +/* Pointer to an int, in which the */ +/* minimum width of a URL item is */ +/* returned, in OS units. */ +/* */ +/* Assumes: Any of the pointers may be NULL. */ +/*************************************************/ + +static _kernel_oserror * hotlist_get_entry_sizes(unsigned int * item_height, unsigned int * item_dir_width, unsigned int * item_url_width) +{ + _kernel_oserror * e; + int width, height; + + /* Get the open directory sprite size */ + + RetError(read_sprite_size(OPEN_DIRECTORY_SPRITE, &width, &height)); + + /* Use this for the minimum width of a directory entry */ + /* and the height of an item. */ + + if (item_dir_width) *item_dir_width = width; + if (item_height) *item_height = height; + + /* Now read the closed sprite; if the size is greater */ + /* than the width or height found above, use the new */ + /* sizes instead. */ + + RetError(read_sprite_size(CLOSED_DIRECTORY_SPRITE, &width, &height)); + + if (item_height && height > *item_height) *item_height = height; + if (item_dir_width && width > *item_dir_width) *item_dir_width = width; + + /* Similarly for the insert item sprite. */ + + RetError(read_sprite_size(INSERT_DIRECTORY_SPRITE, &width, &height)); + + if (item_height && height > *item_height) *item_height = height; + if (item_dir_width && width > *item_dir_width) *item_dir_width = width; + + /* Find the URL sprite size, and if required increase the */ + /* minimum entry height again based on this. */ + + RetError(read_sprite_size(URL_SPRITE, &width, &height)); + + if (item_height && height > *item_height) *item_height= height; + + /* Set the URL width to the value found above and add 8 to */ + /* all of them for aesthetics. */ + + if (item_url_width) *item_url_width = width + 8; /* +8 for aesthetics */ + if (item_dir_width) *item_dir_width += 8; + if (item_height) *item_height += 8; + + return NULL; +} + +/*************************************************/ +/* hotlist_unlink() */ +/* */ +/* This function unlinks the passed item from */ +/* the items linked in before and after it. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct to remove. */ +/*************************************************/ + +static void hotlist_unlink(hotlist_item *item) +{ + if (item->parent && item->parent->data.directory_content == item) item->parent->data.directory_content = item->next; + if (item->previous) item->previous->next = item->next; + if (item->next) item->next->previous = item->previous; + + /* Not strictly needed, but added to ensure robustness */ + + item->next = NULL; + item->previous = NULL; + item->parent = NULL; +} + +/*************************************************/ +/* hotlist_link() */ +/* */ +/* This function links the passed item to the */ +/* passed target. */ +/* */ +/* It can link the item in four different ways; */ +/* before or after the target, or if the target */ +/* is a directory, at the beginning or end of */ +/* that directory's contents. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct to link in; */ +/* */ +/* Pointer to the hotlist_item */ +/* struct to link to; */ +/* */ +/* Position to link to - */ +/* HOTLIST_POSITION_BEGINNING */ +/* HOTLIST_POSITION_END */ +/* HOTLIST_POSITION_BEFORE or */ +/* HOTLIST_POSITION_AFTER. */ +/* */ +/* Assumes: The item and target pointers are */ +/* not NULL and are valid; */ +/* */ +/* That if adding to the beginning */ +/* or end, the target is a directory */ +/* (in both cases, an error will be */ +/* raised in TRACE builds if the */ +/* assumptions are violated). */ +/*************************************************/ + +static _kernel_oserror * hotlist_link(hotlist_item * item, hotlist_item * target, unsigned int position) +{ + #ifdef TRACE + + /* Test certain basic assumptions are not violated */ + + if (!item || !target) + { + erb.errnum = Utils_Error_Custom_Normal; + strcpy(erb.errmess, "NULL hotlist item or target pointer in hotlist_link"); + show_error_ret(&erb); + return NULL; + } + + if ( + ( + position == HOTLIST_POSITION_BEGINNING || + position == HOTLIST_POSITION_END + ) + && target->type != hl_directory + ) + { + erb.errnum = Utils_Error_Custom_Normal; + strcpy(erb.errmess, "Cannot insert at item at the beginning or end when the target is not a directory, in hotlist_link"); + show_error_ret(&erb); + return NULL; + } + + #endif + + switch (position) + { + case HOTLIST_POSITION_BEFORE: + { + /* Simple insertion above the target item */ + + item->next = target; + item->previous = target->previous; + item->parent = target->parent; + + target->previous = item; + + if (item->previous) item->previous->next = item; + if (item->parent && item->parent->data.directory_content == target) item->parent->data.directory_content = item; + } + break; + + default: /* We'll allow a default to adding after the target item */ + + case HOTLIST_POSITION_AFTER: + { + /* Again, a simple insertion after the target item */ + + item->previous = target; + item->next = target->next; + item->parent = target->parent; + + target->next = item; + + if (item->next) item->next->previous = item; + } + break; + + case HOTLIST_POSITION_BEGINNING: + { + /* A bit harder - insert at the top of a directory. */ + /* First a quick sanity check - TRACE builds will */ + /* already have faulted this, but For non-TRACE */ + /* builds, at least this will stop things dying. */ + + if (target->type != hl_directory) return NULL; + + /* This item is going to be at the beginning of a */ + /* directory, so there is never a previous item. */ + + item->previous = NULL; + + /* The next item is the one that used to be at the top */ + /* of the directory, and the parent for this item will */ + /* obviously be the target. */ + + item->next = target->data.directory_content; + item->parent = target; + + /* If we have a next item, make sure its previous field */ + /* now points to the new item at the top. */ + + if (item->next) item->next->previous = item; + + /* The target should point to the new top item, too. */ + + target->data.directory_content = item; + } + break; + + case HOTLIST_POSITION_END: + { + hotlist_item * last; + + /* Similarly, add to the end of the directory */ + + if (target->type != hl_directory) return NULL; + + /* As before the parent must be the target item */ + + item->parent = target; + + /* Now look from the start of the directory downwards for */ + /* the last item currently present. */ + + last = target->data.directory_content; + + while(last && last->next) last = last->next; + + /* The new item must point to that one as in its previous field, */ + /* and has no next item to point to. */ + + item->previous = last; + item->next = NULL; + + if (last) + { + /* If there were any items in the directory, make sure that the */ + /* last one points to the new bottom entry. */ + + last->next = item; + } + else + { + /* Otherwise, the parent directory should point to this item */ + + target->data.directory_content = item; + } + } + break; + } + + /* Finished... */ + + return NULL; +} + +/*************************************************/ +/* hotlist_new_directory() */ +/* */ +/* This function creates a new directory */ +/* at the beginning of the given parent. */ +/* */ +/* */ +/* Parameters: Pointer to a hotlist_item of type */ +/* directory that represents the */ +/* parent item for this new entry; */ +/* */ +/* Name of new directory to create; */ +/* */ +/* Position to create the new item */ +/* relative to the parent (as for */ +/* hotlist_link); */ +/* */ +/* Pointer to a pointer to a */ +/* hotlist_item struct, which will */ +/* be filled in with the address of */ +/* the new item (unless there is an */ +/* error returned). */ +/* */ +/* Assumes: That the pointer to a pointer to */ +/* a hotlist_item struct is not NULL */ +/* (it would make little sense to */ +/* allow this...). */ +/*************************************************/ + +static _kernel_oserror * hotlist_new_directory(hotlist_item * parent, char * directory_name, unsigned int position, hotlist_item ** new) +{ + char * perm_dirname; + hotlist_item * item; + _kernel_oserror * e; + + if (!directory_name) return NULL; + + /* Allocate space for the item and its name */ + + item = malloc(sizeof(hotlist_item)); + + if (!item) + { + make_no_memory_error(4); + + return &erb; + } + + perm_dirname = malloc(strlen(directory_name) + 1); + + if (!perm_dirname) + { + free(item); + + make_no_memory_error(5); + + return NULL; + } + + /* Copy the name into the new buffer */ + + strcpy(perm_dirname, directory_name); + + /* Initialise the new hotlist item */ + + item->type = hl_directory; + item->flags = DIRECTORY_FLAGS; + item->name = perm_dirname; + item->data.directory_content = NULL; + + if (parent) + { + /* If we have been given a parent item, link this new */ + /* directory to it. */ + + e = hotlist_link(item, parent, position); + + /* If there's an error, free the item structure and name, */ + /* and return that error. */ + + if (e) + { + free(item); + free(perm_dirname); + + return e; + } + } + else + { + /* If there's no parent item, fill the various pointers */ + /* to other things in the list with NULL. */ + + item->next = NULL; + item->previous = NULL; + item->parent = NULL; + } + + /* Record the new item in the hotlist_newitem static */ + + hotlist_newitem = item; + + /* Return the address of the new item and exit */ + + *new = item; + + return NULL; +} + +/*************************************************/ +/* hotlist_new_url() */ +/* */ +/* This function creates a new url */ +/* at the end of the passed directory */ +/* */ +/* Parameters: Pointer to a hotlist_item struct */ +/* representing a directory to add */ +/* the URL to; */ +/* */ +/* Position within that directory */ +/* to link to (number of items from */ +/* the start); */ +/* */ +/* Description of the URL (e.g. from */ +/* the page title); */ +/* */ +/* Pointer to the URL itself. */ +/*************************************************/ + +static _kernel_oserror * hotlist_new_url(hotlist_item * parent, unsigned int position, char * url_description, char * url) +{ + char * perm_url_desc; + char * perm_url; + hotlist_item * item; + _kernel_oserror * e; + + /* Allocate a new hotlist_item structure */ + + if ((item = malloc(sizeof(hotlist_item))) == NULL) + { + #ifdef TRACE + if (tl & (1<<25)) Printf("Error: Could not allocate room for new URL item\n"); + #endif + + goto hotlist_new_url_no_memory; /* See bottom of function */ + } + + /* Allocate space for the description string */ + + perm_url_desc = malloc(strlen(url_description) + 1); + + if (perm_url_desc == NULL) goto hotlist_new_url_no_memory; /* See bottom of function */ + + /* Allocate space for the URL */ + + perm_url = malloc(strlen(url) + 1); + + if (perm_url == NULL) + { + free(perm_url_desc); + goto hotlist_new_url_no_memory; /* See bottom of function */ + } + + /* Copy the given description and URL to the allocated space */ + + strcpy(perm_url_desc, url_description); + strcpy(perm_url, url); + + /* Fill in miscellaneous parts of the hotlist_item structure */ + + item->type = hl_url; + item->flags = URL_FLAGS; + item->name = perm_url_desc; + item->data.url = perm_url; + + /* Link the item to the rest of the hotlist */ + + e = hotlist_link(item, parent, position); + + if (e) + { + free(item); + free(perm_url_desc); + free(perm_url); + + return e; + } + + /* Remember which item was added in the hotlist_newitem static */ + /* and exit with no error. */ + + hotlist_newitem = item; + + return NULL; + + /* Code for a common error case */ + +hotlist_new_url_no_memory: + + erb.errnum = Utils_Error_Custom_Normal; + + StrNCpy0(erb.errmess, + lookup_token("NoMemURL:There is not enough free memory to add another URL to the hotlist.", + 0, + 0)); + + return &erb; +} + +/*************************************************/ +/* hotlist_delete_item() */ +/* */ +/* This function deletes an item from the */ +/* hotlist structure; it will recursively delete */ +/* directories. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct to delete. */ +/*************************************************/ + +static void hotlist_delete_item(hotlist_item * item) +{ + if (item) + { + switch (item->type) + { + /* For a URL item, unlink it from the list */ + /* and free associated memory. */ + + case hl_url: + { + hotlist_unlink(item); + free(item->name); + free(item->data.url); + free(item); + } + break; + + case hl_directory: + { + /* Recursively delete all items in the directory */ + + while (item->data.directory_content) + { + hotlist_delete_item(item->data.directory_content); + } + + /* Now unlink the item */ + + hotlist_unlink(item); + + /* Free associated memory */ + + free(item); + free(item->name); + + /* The deletions will affect whether or not other */ + /* items have subdirectories, so update the */ + /* relevant parts of all items in the list. */ + + hotlist_scan_for_subdirectories(item->parent); + } + break; + + default: + { + #ifdef TRACE + + /* A 'should never happen' case! */ + + erb.errnum = Utils_Error_Custom_Message; + strcpy(erb.errmess, "Unrecognised item type in hotlist_delete_item - possibly memory corruption?"); + show_error_ret(&erb); + + #endif + } + break; + } + } +} + +/*************************************************/ +/* hotlist_move_item() */ +/* */ +/* This function takes an item and moves its */ +/* position within the directory tree. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct to move; */ +/* */ +/* Pointer to the hotlist_item */ +/* structure to move to; */ +/* */ +/* Position relative to that struct */ +/* for the item to go to, as for */ +/* hotlist_link. */ +/* */ +/* Assumes: It is assumed that if the object */ +/* is a directory it is not being */ +/* moved into it self or one of its */ +/* children. If this is not the case */ +/* then both it and its children */ +/* will become unlinked from the */ +/* hotlist structure (nasty...). */ +/*************************************************/ + +static _kernel_oserror * hotlist_move_item(hotlist_item * source, hotlist_item * target, unsigned int position) +{ + /* Unlink item from directory structure */ + + hotlist_unlink(source); + + /* Link into new position in directory structure */ + + return hotlist_link(source, target, position); +} + +/*************************************************/ +/* hotlist_copy_item() */ +/* */ +/* This function copies an item, and in the case */ +/* of it being a directory, its children, to the */ +/* specified place. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct to copy; */ +/* */ +/* Pointer to the hotlist_item */ +/* struct to copy to; */ +/* */ +/* Position relative to that item to */ +/* copy to, as for hotlist_link; */ +/* */ +/* Pointer to a pointer to a */ +/* hotlist_item struct, in which the */ +/* address of the new item is */ +/* returned. */ +/* */ +/* Assumes: It is assumed that if the object */ +/* is a directory it is not being */ +/* copied into it self or one of its */ +/* children - if it is the function */ +/* will keep recursing and */ +/* eventually run out of stack; */ +/* */ +/* The pointer to the pointer to the */ +/* hotlist_item struct may be NULL. */ +/*************************************************/ + +static _kernel_oserror * hotlist_copy_item(hotlist_item * source, hotlist_item * target, + unsigned int position, hotlist_item ** new_item) +{ + _kernel_oserror * e; + hotlist_item * newdir; + + newdir = NULL; + + switch(source->type) + { + /* For a URL, create a new item based on the source one */ + + case hl_url: + { + return hotlist_new_url(target, position, source->name, source->data.url); + } + break; + + /* For a directory, first create a new item based on the source one */ + + case hl_directory: + { + hotlist_item * content; + + RetError(hotlist_new_directory(target, source->name, position, &newdir)); + + /* Now copy the contents recursively */ + + content = source->data.directory_content; + + while (content) + { + RetError(hotlist_copy_item(content, newdir, HOTLIST_POSITION_END, NULL)); + + content = content->next; + } + } + break; + } + + /* Fill in the new_item return value */ + + if (new_item) + { + if (newdir) *new_item = newdir; + else *new_item = hotlist_newitem; + } + + return NULL; +} + +/*************************************************/ +/* hotlist_scan_for_subdirectories() */ +/* */ +/* This function will scan a directory contents */ +/* for any subdirectories. If it finds one it */ +/* will set the directory's */ +/* HOTLIST_HAS_SUBDIRECTORY bit, else it unsets */ +/* the bit. */ +/* */ +/* Parameters: Pointer to the hotlist_item */ +/* struct representing the directory */ +/* to scan. */ +/*************************************************/ + +static void hotlist_scan_for_subdirectories(hotlist_item * item) +{ + hotlist_item * list; + + /* Only proceed if we've been given an item and it */ + /* is a directory */ + + if (item && item->type == hl_directory) + { + list = item->data.directory_content; + + /* Go through the contents looking for a directory */ + /* within - as soon as one is found we can set the */ + /* bit and exit */ + + while(list) + { + if (list->type == hl_directory) + { + item->flags |= HOTLIST_D_HAS_SUBDIRECTORY; + return; + } + list = list->next; + } + + /* If the loop exits there were no directories inside */ + /* the given one, so unset the bit. */ + + item->flags &= ~HOTLIST_D_HAS_SUBDIRECTORY; + } +} + +/*************************************************/ +/* _hotlist_find_item() */ +/* */ +/* This function does all the work for */ +/* hotlist_find_item() */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* the n'th item to return */ +/* */ +/* Returns: Pointer to the requested item or */ +/* NULL if it does not exist */ +/*************************************************/ + +static hotlist_item * _hotlist_find_item(hotlist_item * list, unsigned int item_no) +{ + hotlist_item * temp; + while(list) + { + if (item_no == item_number) return list; /* Found the list item */ + item_number++; + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + { + temp = _hotlist_find_item(list->data.directory_content, item_no); + if (temp) return temp; + } + list = list->next; + + } + return NULL; /* List does not extend far enough */ +} + +/*************************************************/ +/* hotlist_find_item() */ +/* */ +/* This function will recursivly scan through */ +/* a hotlist structure and return a pointer to */ +/* the n'th item. It will only recurse through */ +/* open directories. */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* the n'th item to return */ +/* */ +/* Returns: Pointer to the requested item or */ +/* NULL if it does not exist */ +/* */ +/* Note: This function doesn't actually */ +/* do any of the real work but */ +/* merely sets up a global variable */ +/* and calls _hotlist_find_item() */ +/*************************************************/ + +static hotlist_item * hotlist_find_item(hotlist_item *list, unsigned int item_no) +{ + item_number = 0; + return _hotlist_find_item(list, item_no); +} + +/*************************************************/ +/* _hotlist_find_no_from_item() */ +/* */ +/* This function does all the work for */ +/* hotlist_find_no_from_item() */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* the item whose position should be */ +/* returned. */ +/* */ +/* Returns: The item position or */ +/* -1 if it does not exist */ +/*************************************************/ + +static int _hotlist_find_no_from_item(hotlist_item *list, hotlist_item *item) +{ + int temp; + while(list) + { + if (item == list) return item_number; /* Found the list item */ + item_number++; + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + { + temp = _hotlist_find_no_from_item(list->data.directory_content, item); + if (temp >= 0) return temp; + } + list = list->next; + + } + return -1; +} + +/*************************************************/ +/* hotlist_find_no_from_item() */ +/* */ +/* This function will recursivly scan through */ +/* a hotlist structure and return the position */ +/* of the specified item. It will only recurse */ +/* through open directories. */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* Pointer to the item to find */ +/* */ +/* Returns: Position of the requested item or */ +/* -1 if it does not exist */ +/* */ +/* Note: This function doesn't actually */ +/* do any of the real work but */ +/* merely sets up a global variable */ +/* and calls */ +/* _hotlist_find_no_from_item() */ +/*************************************************/ + +static int hotlist_find_no_from_item(hotlist_item *item) +{ + item_number = 0; + return _hotlist_find_no_from_item(hotlist_root->data.directory_content, item); +} + +/************************************************/ +/* _hotlist_find_selected_item() */ +/* */ +/* This function does the work for */ +/* hotlist_find_selected_item() */ +/************************************************/ +hotlist_item * _hotlist_find_selected_item(hotlist_item *list) +{ + hotlist_item *temp; + while(list) + { + if (list->flags & HOTLIST_G_IS_SELECTED) return list; + if (list->type == hl_directory) + { + temp = _hotlist_find_selected_item(list->data.directory_content); + if (temp) return temp; + } + list = list->next; + } + return NULL; +} + +/*************************************************/ +/* hotlist_find_selected_item() */ +/* */ +/* This function returns a pointer to the first */ +/* selected item found */ +/*************************************************/ + +static hotlist_item * hotlist_find_selected_item(void) +{ + return _hotlist_find_selected_item(hotlist_root); +} + +/*************************************************/ +/* _hotlist_count_displayed_items() */ +/* */ +/* This routine does all the work for */ +/* hotlist_count_displayed_items() */ +/*************************************************/ + +static void _hotlist_count_displayed_items(hotlist_item *list) +{ + while(list) + { + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + + { + _hotlist_count_displayed_items(list->data.directory_content); + } + list = list->next; + item_number++; + } +} + +/*************************************************/ +/* hotlist_count_displayed_items() */ +/* */ +/* This routine counts the number of items to be */ +/* displayed in the hotlist window */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* */ +/* Returns: the number of items visible in */ +/* the list */ +/*************************************************/ + +static unsigned int hotlist_count_displayed_items(hotlist_item *list) +{ + item_number = 0; + _hotlist_count_displayed_items(list); + return item_number; +} + +/*************************************************/ +/* hotlist_set_flags() */ +/* */ +/* This function will recursivly set flags for */ +/* either a specified type of hotlist_item or */ +/* all hotlist_items if hl_ALL is specified */ +/* all items which are changed will also have */ +/* their HOTLIST_G_REDRAW_NOW bit set */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* type of hotlist_item to set flags */ +/* for */ +/* flags to set */ +/* */ +/* Returns: 1 if any flags were set */ +/* 0 if no flags were set */ +/*************************************************/ + +static int hotlist_set_flags(hotlist_item *list, hotlist_type type, unsigned int flags) +{ + int changed; + changed = 0; + while(list) + { + if (type == hl_ALL || type == list->type) + { + if (list->flags | flags != list->flags) + { + list->flags |= HOTLIST_G_REDRAW_NOW; + changed = 1; + } + list->flags |= flags; + } + if (list->type == hl_directory) + { + if (hotlist_set_flags(list->data.directory_content, type, flags)) changed = 1; + } + list = list->next; + } + return changed; +} + +/*************************************************/ +/* hotlist_clear_flags() */ +/* */ +/* This function will recursivly clear flags for */ +/* either a specified type of hotlist_item or */ +/* all hotlist_items if hl_ALL is specified */ +/* all items which are changed will also have */ +/* their HOTLIST_G_REDRAW_NOW bit set except if */ +/* the routine is set to clear the */ +/* HOTLIST_G_REDRAW_NOW bit */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* type of hotlist_item to clear */ +/* flags for */ +/* flags to clear */ +/* */ +/* Returns: 1 if any flags were cleared */ +/* 0 if no flags were cleared */ +/*************************************************/ + +static int hotlist_clear_flags(hotlist_item *list, hotlist_type type, unsigned int flags) +{ + int changed; + changed = 0; + while(list) + { + if (type == hl_ALL || type == list->type) + { + if (list->flags & ~flags != list->flags) + { + list->flags |= HOTLIST_G_REDRAW_NOW; + changed = 1; + } + list->flags &= ~flags; + } + if (list->type == hl_directory) + { + if (hotlist_clear_flags(list->data.directory_content, type, flags)) changed = 1; + } + list = list->next; + } + return changed; +} + +/*************************************************/ +/* _hotlist_draw() */ +/* */ +/* This function does all the hard work for */ +/* hotlist_draw() */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* First item number to draw */ +/* Last item number to draw */ +/* Level of indentation */ +/*************************************************/ + +static void _hotlist_draw(hotlist_item *list, + unsigned int first_item, + unsigned int last_item, + unsigned int indent) +{ + WimpIconBlock hotlist_iconblock; + unsigned int item_height, item_dir_width, item_url_width; + unsigned int temp_width; + _kernel_swi_regs regs; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + while(list) + { + if (item_number > last_item) return; /* Break out if done all items to be displayed */ + if (item_number >= first_item) + { + /* draw it */ + + hotlist_iconblock.flags = HOTLIST_SPRITE_ICON_FLAGS; + if (list->flags & HOTLIST_G_IS_SELECTED) hotlist_iconblock.flags |= WimpIcon_Selected; + + hotlist_iconblock.data.is.sprite_area = (void*)sprite_block; + + switch(list->type) /* Set appropriate sprite and width of sprite */ + { + case hl_url: + hotlist_iconblock.data.is.sprite = URL_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(URL_SPRITE); + temp_width = item_url_width; + break; + + case hl_directory: + temp_width = item_dir_width; + if (list->flags & HOTLIST_D_IS_HIGHLIGHTED) + { + hotlist_iconblock.data.is.sprite = INSERT_DIRECTORY_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(INSERT_DIRECTORY_SPRITE); + } + else + { + if (list->flags & HOTLIST_D_IS_OPEN) + { + hotlist_iconblock.data.is.sprite = OPEN_DIRECTORY_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(OPEN_DIRECTORY_SPRITE); + } + else + { + hotlist_iconblock.data.is.sprite = CLOSED_DIRECTORY_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(CLOSED_DIRECTORY_SPRITE); + } + } + break; + + default: + temp_width = 0; /* Only here to stop warnings */ + } + + hotlist_iconblock.bbox.xmin = indent; + hotlist_iconblock.bbox.xmax = indent + temp_width; + hotlist_iconblock.bbox.ymax = (-item_height*item_number); + hotlist_iconblock.bbox.ymin = ((-item_height*item_number) - item_height); + + wimp_plot_icon(&hotlist_iconblock); /* Plot sprite icon */ + + regs.r[0] = 1; + regs.r[1] = (int)list->name; + regs.r[2] = 0; + wimp_text_op(®s); /* Get width of text for text icon */ + + + if (list->flags & HOTLIST_G_IS_SELECTED) + hotlist_iconblock.flags = HOTLIST_TEXT_ICON_FLAGS_SELECTED; + else + hotlist_iconblock.flags = HOTLIST_TEXT_ICON_FLAGS_UNSELECTED; + + hotlist_iconblock.bbox.xmin = indent+temp_width+2; + hotlist_iconblock.bbox.xmax = indent+temp_width+2+regs.r[0] + 12; + hotlist_iconblock.bbox.ymax = (-item_height*item_number) - 2; + hotlist_iconblock.bbox.ymin = (-item_height*item_number) - item_height + 2; + hotlist_iconblock.data.it.buffer = list->name; + hotlist_iconblock.data.it.buffer_size = strlen(list->name); + + wimp_plot_icon(&hotlist_iconblock); + + /*! drawit */ + } + + item_number++; + + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + { + _hotlist_draw(list->data.directory_content, first_item, last_item, indent + item_dir_width); + } + list = list->next; + } +} + +/*************************************************/ +/* hotlist_draw() */ +/* */ +/* This function will recursivly draw the */ +/* hotlist */ +/* */ +/* Parameters: Pointer to a hotlist_item */ +/* First item number to draw */ +/* Last item number to draw */ +/* */ +/* Note: This function doesn't actually */ +/* do any of the real work but */ +/* merely sets up global variables */ +/* and calls _hotlist_draw() */ +/*************************************************/ + +static void hotlist_draw(hotlist_item *list, + unsigned int first_item, + unsigned int last_item) +{ + item_number = 0; + _hotlist_draw(list, first_item, last_item, 0); +} + +/*************************************************/ +/* _hotlist_get_max_width */ +/* */ +/* This function does all the work for */ +/* hotlist_get_max_width */ +/*************************************************/ + +static void _hotlist_get_max_width(hotlist_item *list, unsigned int indent) +{ + unsigned int item_height, item_dir_width, item_url_width; + unsigned int temp_width; + _kernel_swi_regs regs; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + while(list) + { + switch(list->type) + { + case hl_directory: + temp_width = item_dir_width; + break; + + case hl_url: + temp_width = item_url_width; + break; + + default: + temp_width = 0; + break; + } + + regs.r[0] = 1; + regs.r[1] = (int)list->name; + regs.r[2] = 0; + wimp_text_op(®s); + + temp_width += indent+2+regs.r[0] + 12; + + if (temp_width > item_number) item_number = temp_width; + + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + { + _hotlist_get_max_width(list->data.directory_content, indent + item_dir_width); + } + list = list->next; + item_number++; + } +} + +/*************************************************/ +/* hotlist_get_max_width() */ +/* */ +/* This function returns the maximum width of */ +/* of the displayed hotlist entries */ +/* */ +/* */ +/*************************************************/ + +static unsigned int hotlist_get_max_width(hotlist_item *list) +{ + item_number = 0; + _hotlist_get_max_width(list, 0); + return item_number; +} + +/*************************************************/ +/* _hotlist_redraw_now() */ +/* */ +/* This function does the work for */ +/* hotlist_redraw_now() */ +/*************************************************/ + +static void _hotlist_redraw_now(hotlist_item *list) +{ + while(list) + { + if (list->flags & HOTLIST_G_REDRAW_NOW) + { + hotlist_redraw_items(item_number, item_number); + list->flags &= ~HOTLIST_G_REDRAW_NOW; + } + item_number++; + if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) + { + _hotlist_redraw_now(list->data.directory_content); + } + list = list->next; + } +} + +/*************************************************/ +/* hotlist_redraw_now() */ +/* */ +/* This function redraws all visible items with */ +/* the HOTLIST_G_REDRAW_NOW bit set */ +/*************************************************/ + +static void hotlist_redraw_now(void) +{ + item_number = 0; + _hotlist_redraw_now(hotlist_root->data.directory_content); +} + +/*************************************************/ +/* hotlist_add() */ +/* */ +/* Add a new URL to the hotlist. */ +/* */ +/* Parameters: Description of the URL (e.g. from */ +/* the page title); */ +/* */ +/* Pointer to the URL itself; */ +/* */ +/* 0 to add to the top, 1 to add to */ +/* the bottom. */ +/*************************************************/ + +_kernel_oserror * hotlist_add(char * description, char * url, int at_bottom) +{ + _kernel_oserror * e = NULL; + int position; + + position = at_bottom ? HOTLIST_POSITION_END : HOTLIST_POSITION_BEGINNING; + + /* Add the item and ensure the window extent etc. is correct */ + + e = hotlist_new_url(hotlist_root, + position, + description, + url); + + if (!e) hotlist_window_preopen(already_open); + + /* Optimise the redraw to do as little as possible depending */ + /* upon where in the list the item was added. */ + + switch (position) + { + default: + case HOTLIST_POSITION_END: + { + hotlist_redraw_now(); + } + break; + + case HOTLIST_POSITION_BEGINNING: + { + hotlist_redraw_items(0, + hotlist_count_displayed_items(hotlist_root->data.directory_content)); + } + break; + } + + return e; +} + +/*************************************************/ +/* hotlist_get_shape() */ +/* */ +/* This function calculates the xmin and xmax */ +/* of the passed hotlist_item */ +/* */ +/* Parameters: *xmin */ +/* *xmax */ +/* item */ +/*************************************************/ + +static void hotlist_get_shape(unsigned int *xmin, + unsigned int *xmax, + hotlist_item *item) +{ + unsigned int item_height, item_dir_width, item_url_width; + int count = 0, temp_width; + _kernel_swi_regs regs; + hotlist_item *tempitem; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + tempitem = item; + while(tempitem) + { + tempitem = tempitem->parent; + count++; + } + + count -= 2; + + regs.r[0] = 1; + regs.r[1] = (int)item->name; + regs.r[2] = 0; + wimp_text_op(®s); + + switch(item->type) + { + case hl_directory: + temp_width = item_dir_width; + break; + + case hl_url: + temp_width = item_url_width; + break; + + default: + temp_width = 0; + break; + } + + *xmin = count * item_dir_width; + *xmax = count * item_dir_width + temp_width + regs.r[0] + 2 + 12; +} + +/*************************************************/ +/* hotlist_directory_open_close() */ +/* */ +/* This function opens or closes a directory */ +/* along with all required redrawing */ +/* */ +/* Parameters: hotlist_item */ +/* item number on screen */ +/*************************************************/ + +static void hotlist_directory_open_close(hotlist_item *item, unsigned int itemno) +{ + unsigned int item_height, item_dir_width, item_url_width; + int top, window_handle; + BBox bbox; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + item->flags ^= HOTLIST_D_IS_OPEN; + hotlist_window_preopen(already_open); + show_error(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); + + window_get_extent(0, hotlist_windowid, &bbox); + + top = -itemno * item_height; /* Window relative coordinate */ + + if (item->data.directory_content) + { + show_error(wimp_force_redraw(window_handle, + bbox.xmin, + bbox.ymin, + bbox.xmax, + top)); + } + else + { + show_error(wimp_force_redraw(window_handle, + bbox.xmin, + top-item_height, + bbox.xmax, + top)); + } +} + +/*************************************************/ +/* hotlist_redraw_items() */ +/* */ +/* This function forces the redraw of a set of */ +/* items */ +/* */ +/* Parameters: itemno */ +/*************************************************/ + +static void hotlist_redraw_items(unsigned int firstitem, unsigned int lastitem) +{ + unsigned int item_height, item_dir_width, item_url_width; + BBox bbox; + int window_handle; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + show_error(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); + window_get_extent(0, hotlist_windowid, &bbox); + + wimp_force_redraw(window_handle, + bbox.xmin, + - (lastitem + 1) * item_height, + bbox.xmax, + - firstitem * item_height); +} + +/*************************************************/ +/* hotlist_clear_selection() */ +/* */ +/* This function unselects all items and redraws */ +/* appropriate items */ +/*************************************************/ + +static void hotlist_clear_selection(void) +{ + hotlist_clear_flags(hotlist_root->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); + hotlist_redraw_now(); + number_selected = 0; +} + +/*************************************************/ +/* hotlist_launch_url() */ +/* */ +/* This function launches the url in the passed */ +/* item */ +/* */ +/* Parameters: hotlist_item */ +/*************************************************/ + +static void hotlist_launch_url(hotlist_item * item) +{ + #ifdef TRACE + if (tl & (1u<<25)) hotlist_display_item(item); + #endif + + windows_create_browser(item->data.url, NULL, NULL, NULL, 0); +} + +/*************************************************/ +/* hotlist_process_click_on_item() */ +/* */ +/* This function handles mouse clicks on hotlist */ +/* items */ +/*************************************************/ + +static _kernel_oserror * hotlist_process_click_on_item(unsigned int itemno, + hotlist_item *item, + int buttons, + int x, + int y) +{ + switch(buttons) + { + case Wimp_MouseButtonSelect: + if (last_selected_item == itemno) + { + switch(item->type) + { + case hl_directory: + hotlist_directory_open_close(item, itemno); + break; + + case hl_url: + hotlist_launch_url(item); + break; + } + item->flags &= ~HOTLIST_G_IS_SELECTED; + number_selected--; + hotlist_redraw_items(itemno, itemno); + } + break; /* Double click select */ + + case Wimp_MouseButtonAdjust: + if (last_selected_item == itemno) + { + switch(item->type) + { + case hl_directory: + hotlist_directory_open_close(item, itemno); + break; + + case hl_url: + hotlist_launch_url(item); + toolbox_hide_object(0, hotlist_windowid); + break; + } + item->flags &= ~HOTLIST_G_IS_SELECTED; + number_selected--; + hotlist_redraw_items(itemno, itemno); + } + break; /* Double click adjust */ + + case 64: /* Drag select */ + hotlist_start_drag(); + break; + + case 16: /* Drag adjust */ + if (!(item->flags & HOTLIST_G_IS_SELECTED)) + { + item->flags |= HOTLIST_G_IS_SELECTED; + number_selected++; + + selected_parent = item->parent; + last_selected_item = itemno; + } + hotlist_start_drag(); + hotlist_redraw_items(itemno, itemno); + break; + + case 1024: /* Click select */ + if (item->flags & HOTLIST_G_IS_SELECTED) + { + /* Do nothing when selected */ + } + else + { + hotlist_clear_selection(); + item->flags |= HOTLIST_G_IS_SELECTED; + selected_parent = item->parent; + number_selected++; + hotlist_redraw_items(itemno, itemno); + } + last_selected_item = itemno; + break; + + case 256: /* Click adjust */ + if (selected_parent != item->parent && item->type != hl_url) hotlist_clear_selection(); + item->flags ^= HOTLIST_G_IS_SELECTED; + if (item->flags & HOTLIST_G_IS_SELECTED) + { + number_selected++; + } + else + { + number_selected--; + } + hotlist_redraw_items(itemno, itemno); + selected_parent = item->parent; + last_selected_item = itemno; + break; + + } + + return NULL; +} + +/*************************************************/ +/* hotlist_process_click() */ +/* */ +/* This function handles mouse clicks on the */ +/* hotlist window */ +/* */ +/* Parameters: x position */ +/* y position */ +/* button state */ +/*************************************************/ + +static _kernel_oserror * hotlist_process_click(int x, int y, int buttons) +{ + unsigned int item_height, item_dir_width, item_url_width; + hotlist_item *item; + unsigned int xmin, xmax, itemno; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + itemno = -y / item_height; + item = hotlist_find_item(hotlist_root->data.directory_content, itemno); + if (item) hotlist_get_shape(&xmin, &xmax, item); + + if (item && x >= xmin && x <= xmax) + { + return hotlist_process_click_on_item(itemno, item, buttons, x, y); + } + else + { + switch(buttons) + { + case Wimp_MouseButtonSelect: + break; /* Double click select */ + + case Wimp_MouseButtonAdjust: + break; /* Double click adjust */ + + case 64: /* Drag select */ + hotlist_clear_selection(); + hotlist_selection_box_start(); + break; + + case 16: /* Drag adjust */ + hotlist_clear_selection(); + hotlist_selection_box_start(); + break; + + case 1024: + hotlist_clear_selection(); + last_selected_item = 0xffffffff; + break; /* Click select */ + + case 256: + last_selected_item = 0xffffffff; + break; /* Click adjust */ + } + } + return NULL; +} + +/*************************************************/ +/* hotlist_window_preopen() */ +/* */ +/* This function should be called before opening */ +/* the hotlist window. When opentype is */ +/* not_open the window is opened as large as is */ +/* required to show all items. When opentype is */ +/* already_open the window size will grow enough */ +/* to allow all items to fit but its screen size */ +/* will not change, if the window needs to */ +/* it will do so but its work area will not get */ +/* any smaller than the current visible area */ +/* */ +/* Parameters: opentype */ +/* */ +/*************************************************/ + +static void hotlist_window_preopen(hl_opentype type) +{ + unsigned int item_height, item_dir_width, item_url_width; + unsigned int number, maxlen; + BBox bbox; + WimpGetWindowStateBlock state; + int height, width; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + number = hotlist_count_displayed_items(hotlist_root->data.directory_content); + maxlen = hotlist_get_max_width(hotlist_root->data.directory_content) + 4; + + show_error(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); + wimp_get_window_state(&state); + + if (number < HOTLIST_WINDOW_MIN_HEIGHT) number = HOTLIST_WINDOW_MIN_HEIGHT; + if (maxlen < HOTLIST_WINDOW_MIN_WIDTH) maxlen = HOTLIST_WINDOW_MIN_WIDTH; + + if (type == already_open) + window_get_extent(0, hotlist_windowid, &bbox); + + bbox.ymin = -number * item_height; + bbox.xmax = maxlen; + + if (type == already_open) + { + width = state.visible_area.xmax - state.visible_area.xmin; + height = state.visible_area.ymax - state.visible_area.ymin + hotlist_bbar_size; + + if (bbox.ymin > -height) bbox.ymin = -height; + if (bbox.xmax < width) bbox.xmax = width; + } + + bbox.ymax = hotlist_bbar_size; + bbox.xmin = 0; + + window_set_extent(0, hotlist_windowid, &bbox); + + if (type == already_open) + { + show_error(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); + wimp_get_window_state(&state); + wimp_open_window((WimpOpenWindowBlock*)&state); + } +} + +/*************************************************/ +/* hotlist_redraw_handler() */ +/* */ +/* This function redraws the hotlist window */ +/*************************************************/ + +static int hotlist_redraw_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle) +{ + unsigned int item_height, item_dir_width, item_url_width; + unsigned int first_item, last_item; + int more; + WimpRedrawWindowBlock block; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + block.window_handle = event->redraw_window_request.window_handle; + + wimp_redraw_window(&block, &more); + while(more) + { + first_item = -(block.redraw_area.ymax - (block.visible_area.ymax - block.yscroll)) / item_height; + last_item = (-(block.redraw_area.ymin - (block.visible_area.ymax - block.yscroll)) / item_height) + 1; + + hotlist_draw(hotlist_root->data.directory_content, + first_item, + last_item); + wimp_get_rectangle(&block, &more); + } + return 0; +} + +/*************************************************/ +/* hotlist_mouse_click_handler() */ +/* */ +/* This function handles mouse clicks in the */ +/* hotlist window */ +/*************************************************/ + +static int hotlist_mouse_click_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle) +{ + WimpGetWindowStateBlock state; + + state.window_handle = event->mouse_click.window_handle; + wimp_get_window_state(&state); + + wimp_set_caret_position(state.window_handle, -1, 0, 0, -1, -1); + + hotlist_process_click(event->mouse_click.mouse_x + (state.xscroll - state.visible_area.xmin), + event->mouse_click.mouse_y + (state.yscroll - state.visible_area.ymax), + event->mouse_click.buttons); + return 0; +} + +/*************************************************/ +/* hotlist_menuclose_handler() */ +/* */ +/* This function handles closed menu events */ +/*************************************************/ + +static int hotlist_menuclose_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + ObjectId submenu_id; + if (number_selected == -1) + { + hotlist_clear_selection(); + number_selected = 0; + } + show_error(menu_get_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, &submenu_id)); + if (submenu_id) show_error(toolbox_delete_object(0, submenu_id)); + menu_set_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, 0); + return 0; +} + + + +void hotlist_setmenu_details(ObjectId menuid) +{ + hotlist_item * item; + char entrytext[32]; + ObjectId submenu_id; + + switch(number_selected) + { + case 0: + menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, "URL ''"); + menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 1); + menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 1); /* Fade clear selection */ + break; + + case -1: + case 1: + item = hotlist_find_selected_item(); /* One item selected */ + + switch(item->type) + { + case hl_directory: + strcpy(entrytext, "Dir. '"); + toolbox_create_object(0, "HLDirmenu", &submenu_id); + menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id); + break; + + case hl_url: + strcpy(entrytext, "URL '"); + toolbox_create_object(0, "HLURLmenu", &submenu_id); + menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id); + break; + + } + + strncat(entrytext, item->name, 10); + if (strlen(item->name) > 10) strcat(entrytext, "..."); + strcat(entrytext, "'"); + menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, entrytext); + menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 0); /* unfade URL '' */ + menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 0); /* unfade clear selection */ + break; + + default: + menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, "Selection"); /* Multiple items selected */ + menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 0); /* unfade URL '' */ + menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 0); /* unfade clear selection */ + toolbox_create_object(0, "HLSlctmenu", &submenu_id); + menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id); + break; + + } +} + +/*************************************************/ +/* hotlist_menuopen_handler() */ +/* */ +/* This function handles opened menu events */ +/*************************************************/ + +static int hotlist_menuopen_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) +{ + WimpGetWindowStateBlock state; + WimpGetPointerInfoBlock pointerblock; + hotlist_item * item; + unsigned int item_height, item_dir_width, item_url_width; + unsigned int xmin, xmax; + int window_handle; + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + if (event_code == HotlistMenuOpened) + { + window_get_wimp_handle(0, hotlist_windowid, &window_handle); + state.window_handle = window_handle; + wimp_get_window_state(&state); + + wimp_get_pointer_info(&pointerblock); + pointerblock.x = pointerblock.x + (state.xscroll - state.visible_area.xmin); + pointerblock.y = pointerblock.y + (state.yscroll - state.visible_area.ymax); + + menu_itemno = -pointerblock.y / item_height; + } + + if (number_selected == 0) + { + item = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); + + if (item) + { + hotlist_get_shape(&xmin, &xmax, item); + } + + if (item && pointerblock.x >= xmin && pointerblock.x <= xmax) + { + item->flags |= HOTLIST_G_IS_SELECTED; + selected_parent = item->parent; + number_selected = -1; + hotlist_redraw_items(menu_itemno, menu_itemno); + } + } + + hotlist_setmenu_details(id_block->self_id); + + return 0; +} +/*************************************************/ +/* hotlist_save_entries() */ +/* */ +/* This function recurses through the hotlist */ +/* directory structure saving all directories */ +/* and entries as it goes. */ +/* */ +/* Parameters: Pointer to a FILE struct for the */ +/* file to write to; */ +/* */ +/* Pointer to a hotlist_item struct */ +/* representing the first item in */ +/* the directory to save (which may */ +/* itself be a directory). */ +/* */ +/* Assumes: The FILE pointer must not be NULL */ +/* however the hotlist_item pointer */ +/* can be (e.g. an empty directory). */ +/*************************************************/ + +static _kernel_oserror * hotlist_save_entries(FILE * fileptr, hotlist_item * list) +{ + int written; + _kernel_oserror * e; + + /* Write the entry header */ + + HotlistWrite(fprintf(fileptr, "<ul>\n")); + + /* Follow the directory list */ + + while (list) + { + switch (list->type) + { + /* Write a link for URLs */ + + case hl_url: + { + HotlistWrite(fprintf(fileptr, "<li><a href=\"%s\">%s</a>\n", list->data.url, list->name)); + } + break; + + /* Write a heading for directories */ + + case hl_directory: + { + HotlistWrite(fprintf(fileptr, "<h4>%s</h4>\n", list->name)); + + /* Recursive call for the directory contents */ + + RetError(hotlist_save_entries(fileptr, list->data.directory_content)); + } + break; + } + + /* Continue down the list */ + + list = list->next; + } + + /* Write the entry footer */ + + HotlistWrite(fprintf(fileptr, "</ul>\n")); + + return NULL; +} + +/*************************************************/ +/* hotlist_save() */ +/* */ +/* This function saves the hotlist as an HTML */ +/* file. */ +/* */ +/* Parameters: Pointer to the filename to load */ +/* (null terminated). */ +/*************************************************/ + +_kernel_oserror * hotlist_save(char * filename) +{ + FILE * fileptr; + _kernel_oserror * e; + int written; + + /* Open the file for writng */ + + fileptr = fopen(filename, "wb"); + + /* Complain if it fails */ + + if (fileptr == NULL) return _kernel_last_oserror(); + + /* Write the file header */ + + HotlistWrite(fprintf(fileptr, "<html>\n")); + HotlistWrite(fprintf(fileptr, "<head><title>Hotlist</title></head>\n")); + HotlistWrite(fprintf(fileptr, "<body>\n")); + + /* Fill in the body */ + + e = hotlist_save_entries(fileptr, hotlist_root->data.directory_content); + + if (e) + { + fclose(fileptr); + + return e; + } + + /* Write the footer and close the file */ + + HotlistWrite(fprintf(fileptr, "</body>\n")); + HotlistWrite(fprintf(fileptr, "</html>\n")); + + fclose(fileptr); + + /* Set the filetype to HTML (0xfaf) */ + + return _swix(OS_File, + _INR(0,2), + + 18, + filename, + FileType_HTML); +} + +/*************************************************/ +/* hotlist_lower_tags() */ +/* */ +/* This function processes the passed string */ +/* turning all characters within a tag to lower */ +/* case. It will detect characters within quotes */ +/* and leave their case the same; this will */ +/* preserve the case of URLs. */ +/* */ +/* Obviously, this assumes that the HTML file */ +/* being fed in is not broken; tags and quoted */ +/* text must always be correctly closed, and */ +/* in both cases must not span multiple lines. */ +/* */ +/* Parameters: Pointer to the string to process. */ +/*************************************************/ + +static void hotlist_lower_tags(char *string) +{ + int intag = 0, inquotes = 0; + + while (*string) + { + if (intag) + { + if (inquotes) + { + if (*string == '"') inquotes = 0; + } + else + { + if (*string == '"') inquotes = 1; + if (*string == '>') intag --; + + *string = tolower(*string); + } + } + else + { + if (*string == '<') intag++; + } + + string++; + } +} + +/*************************************************/ +/* hotlist_load_directory() */ +/* */ +/* This function loads the directory contents of */ +/* a hotlist HTML file previously saved by */ +/* hotlist_save or a compatible source. For */ +/* example, at the time of creation this can */ +/* correctly load and understand hotlist files */ +/* from at least one other popular browser. */ +/* */ +/* Parameters: Pointer to a FILE struct through */ +/* which data will be read; */ +/* */ +/* Pointer to a hotlist_item; new */ +/* data structures generated from */ +/* the file contents are added to */ +/* the linked list that this struct */ +/* lies in. */ +/*************************************************/ + +static _kernel_oserror * hotlist_load_directory(FILE * fileptr, hotlist_item * target) +{ + _kernel_oserror * e = NULL; + + static char * next_directory_name = NULL; + static char * string_buffer = NULL; + static char * str_ptr; + + char * url; + hotlist_item * new_dir; + unsigned int unfollowed_uls = 0; + long int file_position; + int character, count; + + /* Go through the file in chunks */ + + while (!feof(fileptr)) /* In theory the code below means you'll never get this; but just to be safe... */ + { + file_position = ftell(fileptr); + + /* Scan ahead to find the end of line - marked by any */ + /* control character, in this case; need to include */ + /* as many consecutive control chars as are present */ + /* in the file, in the count. */ + + do + { + /* First, get to either a control char or EOF */ + + character = fgetc(fileptr); + } + while (character > 31 && character != EOF); + + while (character < 32 && character != EOF) + { + /* If we're not on EOF, continue until we're no longer */ + /* on a control char or hit EOF. */ + + character = fgetc(fileptr); + } + + /* Work out how many bytes we've read */ + + count = (int) (ftell(fileptr) - file_position); + + /* If count is zero, we're at the end of the file */ + + if (!count) break; + + /* If we're not on EOF and don't have a control char, then */ + /* we overshot by one; so we would want to subtract one */ + /* from count. However, to ensure string manipulation */ + /* works OK, we'll need one char to null terminate the */ + /* string. So in that case, we need to *add* one to count */ + /* if the above condition isn't true. */ + + if (!(character > 31 && character != EOF)) count ++; + + /* Right, after all that messing around rewind to the stored */ + /* file position and allocate a buffer for this string. */ + + if (fseek(fileptr, file_position, SEEK_SET)) + { + e = _kernel_last_oserror(); + goto hotlist_load_directory_exit; /* (See near end of function) */ + } + + /* Note that string_buffer is a static, as this way only one */ + /* of these buffers ever exists, even for recursive calls. */ + + if (string_buffer) free(string_buffer); + + string_buffer = malloc(count); + + /* Complain if the allocation fails */ + + if (!string_buffer) + { + make_no_memory_error(2); + + e = &erb; + goto hotlist_load_directory_exit; /* (See near end of function) */ + } + + /* Read the data and force a terminator at the end of the buffer, */ + /* just to be safe. */ + + if (fread(string_buffer, sizeof(char), count - 1, fileptr) != count - 1) + { + e = _kernel_last_oserror(); + goto hotlist_load_directory_exit; /* (See near end of function) */ + } + + string_buffer[count - 1] = 0; + + /* Convert tags to lower case */ + + hotlist_lower_tags(string_buffer); + + /* Treat any opening '<h...>' tag (header) as the title to a directory */ + + str_ptr = strstr(string_buffer, "<h"); + + if ( + str_ptr && + str_ptr[2] >= '1' && + str_ptr[2] <= '6' && + str_ptr[3] == '>' + ) + { + /* Read the directory name (up to the closing '</h...>') */ + + str_ptr = strtok(str_ptr + 4, "<"); + + /* Allocate space for it */ + + if (next_directory_name) free(next_directory_name); + + next_directory_name = malloc(strlen(str_ptr) + 1); + + /* Complain if the allocation fails */ + + if (!next_directory_name) + { + make_no_memory_error(3); + + e = &erb; + goto hotlist_load_directory_exit; /* (See near end of function) */ + } + + /* Otherwise, copy the name in */ + + strcpy(next_directory_name, str_ptr); + } + + /* Treat any '<a href=...>' attribute contents (link) as a URL for the hotlist */ + + else if ((str_ptr = strstr(string_buffer, "<a href=\"")) != NULL) /* Using '!= NULL' stops a compiler warning... */ + { + /* First extract the URL */ + + str_ptr += 9; /* Derived from strlen("<a href=\"") */ + + str_ptr = strtok(str_ptr, "\""); + + /* Because we're about to use strtok() to extract the title, it'll */ + /* put a convenient terminator into the string_buffer block at the */ + /* end of the URL. So all we need to do is record the current */ + /* pointer in the block, str_ptr, for use when we finally add the */ + /* item to the hotlist. */ + + url = str_ptr; + + /* Extract the title - between the closing '>' of the '<a href=...' */ + /* and the opening '<' of the '</a>'. */ + + str_ptr = strtok(NULL, "><"); + + /* Add this item */ + + e = hotlist_new_url(target, HOTLIST_POSITION_END, str_ptr, url); + if (e) goto hotlist_load_directory_exit; /* (See near end of function) */ + } + + /* Treat any '<ul>' tags as the start of a new directory. The name comes */ + /* from a preceeding '<h...>' tag (see above). */ + + else if ((str_ptr = strstr(string_buffer, "<ul>")) != NULL) /* New directory */ + { + if (!next_directory_name) + { + /* If we don't have a directory name for this one, apparently, flag it */ + /* by incrementing the unmatched '<ul>' counter. */ + + unfollowed_uls ++; + } + else + { + /* Otherwise, add the directory */ + + e = hotlist_new_directory(target, next_directory_name, HOTLIST_POSITION_END, &new_dir); + if (e) goto hotlist_load_directory_exit; + + free(next_directory_name); + next_directory_name = NULL; + + /* If the directory was added, recursively load the contents */ + + if (new_dir) + { + /* Note this will invalidate string_buffer. The contents must */ + /* not be used after the call! */ + + e = hotlist_load_directory(fileptr, new_dir); + if (e) goto hotlist_load_directory_exit; /* (See near end of function) */ + } + else + { + /* If the directory was not added, flag it */ + + unfollowed_uls ++; + } + } + } + + /* Treat any '</ul>' tags as the end of a directory. */ + + else if ((str_ptr = strstr(string_buffer, "</ul>")) != NULL) /* Close directory */ + { + /* If we have unfollowed '<ul>' tags, decrement the counter; */ + /* otherwise, exit quietly. */ + + if (unfollowed_uls) unfollowed_uls--; + else goto hotlist_load_directory_exit; /* (See near end of function) */ + } + } + + /* This section is not necessarily an error exit condition, so */ + /* the code falls through to it in normal running. If 'e' is */ + /* NULL there's still no error returned in the end. Note */ + /* though how the various buffers are freed, and thus */ + /* invalidated, at this point; so during recursive routines, */ + /* they must not be accessed after the recursive call has */ + /* been made (unless, of course, they are reallocated). */ + +hotlist_load_directory_exit: + + /* Free up the temporary buffers */ + + if (string_buffer) + { + free(string_buffer); + string_buffer = NULL; + } + + if (next_directory_name) + { + free(next_directory_name); + next_directory_name = NULL; + } + + // We could at this point give a warning if unfollowed_uls is non-zero. + + return e; +} + +/*************************************************/ +/* hotlist_load() */ +/* */ +/* This function loads an HTML file previously */ +/* saved by hotlist_save as the new hotlist. */ +/* */ +/* Parameters: Pointer to the filename to load */ +/* (null terminated). */ +/*************************************************/ + +_kernel_oserror * hotlist_load(char * filename) +{ + _kernel_oserror * e; + FILE * fileptr; + + /* First delete all the existing hotlist items */ + + while (hotlist_root->data.directory_content) + { + hotlist_delete_item(hotlist_root->data.directory_content); + } + + /* Open the file */ + + fileptr = fopen(filename, "r"); + + if (fileptr == NULL) + { + erb.errnum = Utils_Error_Custom_Normal; + StrNCpy0(erb.errmess, + lookup_token("HlCantLoad:Hotlist file could not be loaded", + 0, + 0)); + return &erb; + } + + /* Load it (any errors are returned right at the end) */ + + e = hotlist_load_directory(fileptr, hotlist_root); + + fclose(fileptr); + + /* Clear all of the various flags for redraw, */ + /* selection etc. now that we have a new */ + /* hotlist. */ + + hotlist_clear_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); + + #ifdef TRACE + + /* Show the tree now the file is loaded */ + + if (tl & (1u<<25)) hotlist_display_tree(hotlist_root, 0); + + #endif + + /* Finished; redraw issues are left to the caller */ + + return e; +} + +/*************************************************/ +/* hotlist_initialise() */ +/* */ +/* This function initialises the hotlist library */ +/* routines, and must be called before any other */ +/* hotlist functions. */ +/*************************************************/ + +_kernel_oserror * hotlist_initialise(void) +{ + _kernel_oserror * e; + ObjectId toolbar; + ObjectId menu_id; + BBox bbox; + + /* Create root directory item */ + + RetError(hotlist_new_directory(NULL, "Root", 0, &hotlist_root)); + + /* Create the hotlist window */ + + RetError(toolbox_create_object(0, "HotlistWind", &hotlist_windowid)); + + /* Is there a toolbar? */ + + RetError(window_get_tool_bars(InternalTopLeft, + hotlist_windowid, + NULL, + &toolbar, + NULL, + NULL)); + + /* If so, read the size */ + + if (toolbar != 0) + { + RetError(window_get_extent(0, toolbar, &bbox)); + + hotlist_bbar_size = bbox.ymax - bbox.ymin; + } + else hotlist_bbar_size = 0; + + /* Register event handlers for redraw, clicks and drags in the */ + /* main hotlist window. */ + + RetError(event_register_wimp_handler(hotlist_windowid, + Wimp_ERedrawWindow, + hotlist_redraw_handler, + NULL)); + + RetError(event_register_wimp_handler(hotlist_windowid, + Wimp_EMouseClick, + hotlist_mouse_click_handler, + NULL)); + + RetError(event_register_wimp_handler(-1, + Wimp_EUserDrag, + hotlist_drag_completed_handler, + NULL)); + + /* Menu handlers */ + + RetError(window_get_menu(0, hotlist_windowid, &menu_id)); + + RetError(event_register_toolbox_handler(menu_id, + HotlistMenuOpened, + hotlist_menuopen_handler, + NULL)); + + RetError(event_register_toolbox_handler(menu_id, + HotlistMenuClosed, + hotlist_menuclose_handler, + NULL)); + + /* Main menu items */ + + RetError(event_register_toolbox_handler(-1, + HotlistSelectAll, + hotlist_menu_selectall_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistClearSelect, + hotlist_menu_clearselect_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistOpenAll, + hotlist_menu_openall_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistCloseAll, + hotlist_menu_closeall_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistDelete, + hotlist_menu_delete_handler, + NULL)); + + /* Submenu warning events */ + + RetError(event_register_toolbox_handler(-1, + HotlistShowEditURL, + hotlist_show_editurl_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistShowRenameDirectory, + hotlist_show_rendirectory_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistShowNewURL, + hotlist_show_newurl_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistShowNewDirectory, + hotlist_show_newdirectory_handler, + NULL)); + + /* Hotlist related dialogue events */ + + RetError(event_register_toolbox_handler(-1, + HotlistNewEditURLOk, + hotlist_newedit_url_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistNewRenameDirectoryOk, + hotlist_newren_directory_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistNewEditURLCancel, + hotlist_reset_url_handler, + NULL)); + + RetError(event_register_toolbox_handler(-1, + HotlistNewRenameDirectoryCancel, + hotlist_reset_directory_handler, + NULL)); + return NULL; +} + +/*************************************************/ +/* hotlist_open() */ +/* */ +/* Opens the hotlist window. */ +/* */ +/* Parameters: Show type (as for a call to */ +/* Toolbox_ShowObject); */ +/* */ +/* Show block (as for a call to */ +/* Toolbox_ShowObject); */ +/* */ +/* 0 to open showing descriptions, */ +/* or 1 to open showing URLs. */ +/*************************************************/ + +_kernel_oserror * hotlist_open(int show_type, void * type, int show_urls) +{ + // Not implemented yet... + + show_urls = show_urls; + + /* Set the size of the window etc. */ + + hotlist_window_preopen(not_open); + + /* Show the hotlist */ + + return toolbox_show_object(0, + hotlist_windowid, + show_type, + type, + 0, + 0); +} + +/*************************************************/ +/* hotlist_close() */ +/* */ +/* Closes the hotlist window. */ +/*************************************************/ + +_kernel_oserror * hotlist_close(void) +{ + return toolbox_hide_object(0, hotlist_windowid); +} + +/*************************************************/ +/* hotlist_menu_selectall_handler() */ +/* */ +/* This function handles the select all menu */ +/* item */ +/*************************************************/ + +static int hotlist_menu_selectall_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + ObjectId sub_menu; + + if (!hotlist_root->data.directory_content) return 0; + + item = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); + + if (item && item->parent) + { + item = item->parent->data.directory_content; + number_selected = 0; + while(item) + { + item->flags |= HOTLIST_G_IS_SELECTED | HOTLIST_G_REDRAW_NOW; + number_selected++; + item = item->next; + } + hotlist_redraw_now(); + menu_get_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, &sub_menu); + menu_set_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, 0); + if (sub_menu) toolbox_delete_object(0, sub_menu); + hotlist_setmenu_details(id_block->self_id); + } + return 0; +} + +/*************************************************/ +/* hotlist_menu_clearselection_handler() */ +/* */ +/* This function handles the clear selection */ +/* menu item */ +/*************************************************/ + +static int hotlist_menu_clearselect_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_clear_selection(); + menu_set_entry_text(0, id_block->self_id, 0x05, "URL ''"); + menu_set_fade(0, id_block->self_id, 0x05, 1); /* Fade URL'' selection */ + menu_set_fade(0, id_block->self_id, 0x01, 1); /* Fade clear selection */ + return 0; +} + +/*************************************************/ +/* hotlist_menu_openall_handler() */ +/* */ +/* This function handles the open all menu item */ +/*************************************************/ + +static int hotlist_menu_openall_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + if (hotlist_root->data.directory_content) + { + menu_itemno = 0; + hotlist_set_flags(hotlist_root->data.directory_content, hl_directory, HOTLIST_D_IS_OPEN); + hotlist_clear_flags(hotlist_root->data.directory_content, hl_directory, HOTLIST_G_REDRAW_NOW); + hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content)); + hotlist_window_preopen(already_open); + } + return 0; +} + +/*************************************************/ +/* hotlist_menu_closeall_handler() */ +/* */ +/* This function handles the close all menu */ +/* item */ +/*************************************************/ + +static int hotlist_menu_closeall_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + unsigned int noitems; + if (hotlist_root->data.directory_content) + { + menu_itemno = 0; + noitems = hotlist_count_displayed_items(hotlist_root->data.directory_content); + + hotlist_clear_flags(hotlist_root->data.directory_content, + hl_directory, + HOTLIST_D_IS_OPEN | HOTLIST_G_REDRAW_NOW); + hotlist_clear_selection(); + hotlist_redraw_items(0, noitems); + menu_set_entry_text(0, id_block->self_id, HOTLIST_URL_MENUITEM, "URL ''"); + menu_set_fade(0, id_block->self_id, HOTLIST_URL_MENUITEM, 1); /* Fade URL'' selection */ + menu_set_fade(0, id_block->self_id, HOTLIST_CLEARSELECTION_MENUITEM, 1); /* Fade clear selection */ + hotlist_window_preopen(already_open); + } + return 0; +} + +/*************************************************/ +/* hotlist_menu_delete_handler() */ +/* */ +/* This function handles the delete menu item */ +/*************************************************/ + +static int hotlist_menu_delete_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + unsigned int noitems; + + noitems = hotlist_count_displayed_items(hotlist_root->data.directory_content); + + if (!number_selected) return 0; + + while((item = hotlist_find_selected_item())!=NULL) + { + hotlist_delete_item(item); + } + number_selected = 0; + hotlist_redraw_items(0, noitems); + hotlist_window_preopen(already_open); + toolbox_hide_object(0, id_block->ancestor_id); + return 0; +} + +/*************************************************/ +/* hotlist_show_editurl_handler() */ +/* */ +/* This function fills in the description and */ +/* url fields of the edit url dialogue box */ +/*************************************************/ + +static int hotlist_show_editurl_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + ObjectId dboxid; + +#ifdef TRACE + Printf("hotlist_show_editurl_handler\n"); +#endif + + item = hotlist_find_selected_item(); + if (!item) return 0; + menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid); + alter_new = HOTLIST_MENUSECTION_ALTER; + window_set_title(0, dboxid, "Edit URL"); + writablefield_set_value(0, dboxid, 0x01, item->name); + writablefield_set_value(0, dboxid, 0x05, item->data.url); + actionbutton_set_text(0, dboxid, 0x02, "Alter"); + return 0; +} + +/*************************************************/ +/* hotlist_show_rendirectory_handler() */ +/* */ +/* This function fills in the name field of the */ +/* rename directory dialogue box */ +/*************************************************/ + +static int hotlist_show_rendirectory_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + ObjectId dboxid; + +#ifdef TRACE + Printf("hotlist_show_rendirectory_handler\n"); +#endif + + item = hotlist_find_selected_item(); + if (!item) return 0; + + menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid); + + alter_new = HOTLIST_MENUSECTION_ALTER; + window_set_title(0, dboxid, "Rename Directory"); + writablefield_set_value(0, dboxid, 0x01, item->name); + actionbutton_set_text(0, dboxid, 0x02, "Rename"); + return 0; +} + +/*************************************************/ +/* hotlist_show_newurl_handler() */ +/* */ +/* This function emptys the description and url */ +/* fields of the new url dialogue box */ +/*************************************************/ + +static int hotlist_show_newurl_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + ObjectId dboxid; +#ifdef TRACE + Printf("hotlist_show_newurl_handler\n"); +#endif + + menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid); + + alter_new = HOTLIST_MENUSECTION_NEW; + writablefield_set_value(0, dboxid, 0x01, ""); + writablefield_set_value(0, dboxid, 0x05, ""); + window_set_title(0, dboxid, "Create new URL"); + actionbutton_set_text(0, dboxid, 0x02, "Create"); + return 0; +} + +/*************************************************/ +/* hotlist_show_newdirectory_handler() */ +/* */ +/* This function emptys the name field of the */ +/* new directory dialogue box */ +/*************************************************/ + +static int hotlist_show_newdirectory_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + ObjectId dboxid; +#ifdef TRACE + Printf("hotlist_show_newdirectory_handler\n"); +#endif + + menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid); + + alter_new = HOTLIST_MENUSECTION_NEW; + writablefield_set_value(0, dboxid, 0x01, ""); + window_set_title(0, dboxid, "Create new Directory"); + actionbutton_set_text(0, dboxid, 0x02, "Create"); + return 0; +} + +/*************************************************/ +/* hotlist_newedit_url_handler() */ +/* */ +/* This function either alters the selected urls */ +/* name and url fields or creates a new url in */ +/* the directory the pointer was in when menu */ +/* was clicked in the hotlist window. */ +/*************************************************/ + +static int hotlist_newedit_url_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item, *tempitem; + char *tempdesc, *tempurl; + int size; + + item = hotlist_find_selected_item(); + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWURL_NAME, + NULL, + 0, + &size); + tempdesc = malloc(size); + if (!tempdesc) return 0; /* ERROR can't rename */ + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWURL_NAME, + tempdesc, + size, + &size); + + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWURL_URL, + NULL, + 0, + &size); + tempurl = malloc(size); + if (!tempurl) + { + free(tempdesc); + return 0; /* ERROR can't rename */ + } + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWURL_URL, + tempurl, + size, + &size); + + switch(alter_new) + { + case HOTLIST_MENUSECTION_NEW: + tempitem = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); + if (!tempitem) tempitem = hotlist_root; + else + { + tempitem = tempitem->parent; + } + show_error(hotlist_new_url(tempitem, + HOTLIST_POSITION_END, + tempdesc, + tempurl)); + hotlist_window_preopen(already_open); + hotlist_redraw_now(); + free(tempdesc); + free(tempurl); + break; + + case HOTLIST_MENUSECTION_ALTER: + free(item->name); + free(item->data.url); + item->name = tempdesc; + item->data.url = tempurl; + item->flags |= HOTLIST_G_REDRAW_NOW; + hotlist_window_preopen(already_open); + hotlist_redraw_now(); + //toolbox_hide_object(0, id_block->ancestor_id); + break; + + } + return 0; +} + +/*************************************************/ +/* hotlist_newren_directory_handler() */ +/* */ +/* This function either alters the selected */ +/* directorys name or creates a new directory in */ +/* the directory the pointer was in when menu */ +/* was clicked in the hotlist window. */ +/*************************************************/ + +static int hotlist_newren_directory_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + int size; + hotlist_item *item, *tempitem; + char *tempname; + + item = hotlist_find_selected_item(); + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWDIRECTORY_NAME, + NULL, + 0, + &size); + tempname = malloc(size); + if (!tempname) return 0; /* ERROR can't rename */ + writablefield_get_value(0, + id_block->self_id, + HOTLIST_NEWDIRECTORY_NAME, + tempname, + size, + &size); + + switch(alter_new) + { + case HOTLIST_MENUSECTION_NEW: + tempitem = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); + if (!tempitem) tempitem = hotlist_root; + else + tempitem = tempitem->parent; + hotlist_new_directory(tempitem, + tempname, + HOTLIST_POSITION_END, + &tempitem); + hotlist_window_preopen(already_open); + hotlist_redraw_now(); + free(tempname); + break; + + case HOTLIST_MENUSECTION_ALTER: + free(item->name); + item->name = tempname; + item->flags |= HOTLIST_G_REDRAW_NOW; + hotlist_window_preopen(already_open); + hotlist_redraw_now(); + //toolbox_hide_object(0, id_block->ancestor_id); + break; + + } + return 0; +} + +/*************************************************/ +/* hotlist_newren_directory_handler() */ +/* */ +/* This function is called when Cancel is */ +/* clicked on in the new or edit url dialogue */ +/* box. It resets the contents to their */ +/* previous state. */ +/*************************************************/ + +static int hotlist_reset_url_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + switch(alter_new) + { + case HOTLIST_MENUSECTION_NEW: + writablefield_set_value(0, id_block->self_id, 0x01, ""); + writablefield_set_value(0, id_block->self_id, 0x05, ""); + break; + + case HOTLIST_MENUSECTION_ALTER: + item = hotlist_find_selected_item(); + writablefield_set_value(0, id_block->self_id, 0x01, item->name); + writablefield_set_value(0, id_block->self_id, 0x05, item->data.url); + break; + + } + return 0; +} + +/*************************************************/ +/* hotlist_reset_directory_handler() */ +/* */ +/* This function is called when Cancel is */ +/* clicked on in the new or rename directory */ +/* dialogue box. It resets the contents to */ +/* their previous state. */ +/*************************************************/ + +static int hotlist_reset_directory_handler(int event_code, ToolboxEvent *event, IdBlock *id_block, void *handle) +{ + hotlist_item *item; + switch(alter_new) + { + case HOTLIST_MENUSECTION_NEW: + writablefield_set_value(0, id_block->self_id, 0x01, ""); + break; + + case HOTLIST_MENUSECTION_ALTER: + item = hotlist_find_selected_item(); + writablefield_set_value(0, id_block->self_id, 0x01, item->name); + break; + + } + return 0; +} + +/*************************************************/ +/* hotlist_drag_renderer() */ +/* */ +/* renders a single hotlist item at 0,0 for use */ +/* with draganobject */ +/* */ +/* Parameters: pointer to the item to render */ +/*************************************************/ + +static void hotlist_drag_renderer(hotlist_item *item, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width) +{ + /* DONT PUT Printf's in here, doesn't work (on pipefs anyway) */ + WimpIconBlock hotlist_iconblock; + int temp_width; + _kernel_swi_regs regs; + + hotlist_iconblock.flags = HOTLIST_SPRITE_ICON_FLAGS; + + hotlist_iconblock.data.is.sprite_area = (void*)sprite_block; + + switch(item->type) /* Set appropriate sprite and width of sprite */ + { + case hl_url: + hotlist_iconblock.data.is.sprite = URL_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(URL_SPRITE); + temp_width = item_url_width; + break; + + case hl_directory: + temp_width = item_dir_width; + hotlist_iconblock.data.is.sprite = CLOSED_DIRECTORY_SPRITE; + hotlist_iconblock.data.is.sprite_name_length = strlen(CLOSED_DIRECTORY_SPRITE); + break; + + default: + temp_width = 0; /* Prevent warnings */ + break; + } + + hotlist_iconblock.bbox.xmin = 2; + hotlist_iconblock.bbox.xmax = 2 + temp_width; + hotlist_iconblock.bbox.ymax = 2 + item_height; + hotlist_iconblock.bbox.ymin = 2; + + if (wimp_plot_icon(&hotlist_iconblock)) /* Plot sprite icon */ + { + /* Error has happened, don't know what to do about it */ + } + + regs.r[0] = 1; + regs.r[1] = (int)item->name; + regs.r[2] = 0; + if (wimp_text_op(®s)) /* Get width of text for text icon */ + { + /* Error has happened, don't know what to do about it */ + } + + hotlist_iconblock.flags = HOTLIST_TEXT_ICON_FLAGS_DRAG; + hotlist_iconblock.bbox.xmin = temp_width + 4; + hotlist_iconblock.bbox.xmax = temp_width + regs.r[0] + 16; + hotlist_iconblock.bbox.ymax = item_height; + hotlist_iconblock.bbox.ymin = 4; + hotlist_iconblock.data.it.buffer = item->name; + hotlist_iconblock.data.it.buffer_size = strlen(item->name); + + if (wimp_plot_icon(&hotlist_iconblock)) + { + /* Error has happened, don't know what to do about it */ + } +} + +/*************************************************/ +/* hotlist_start_drag() */ +/* */ +/* This function is called to start a drag */ +/* operation from the hotlist window. A drag */ +/* box will be initiated bounding all selected */ +/* items. */ +/*************************************************/ + +static _kernel_oserror *_hotlist_start_drag(void) +{ + _kernel_swi_regs regs; + _kernel_oserror *err; + unsigned int item_height, item_dir_width, item_url_width; + hotlist_item *item; + WimpDragBox box; + WimpGetPointerInfoBlock pointerblock; + unsigned int xmin, xmax, xlow, xhigh; + int top, bottom, item_no, xorigin, yorigin, screenwidth, screenheight, tempint; + WimpGetWindowStateBlock state; + int redraw_params[4]; + int width, height; + + wimp_get_pointer_info(&pointerblock); /* Read pointer position */ + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + item = hotlist_find_selected_item(); + item_no = hotlist_find_no_from_item(item); + top = -item_no * item_height; + bottom = -(item_no + 1) * item_height; + if (item_no < 0) return NULL; /* Selected item not found, this should never happen */ + + hotlist_get_shape(&xmin, &xmax, item); + item = item->next; + if (number_selected > 1) + { + while(item) + { + item_no++; + hotlist_get_shape(&xlow, &xhigh, item); + if (item->flags & HOTLIST_G_IS_SELECTED) + { + if (xlow < xmin) xmin = xlow; /* Should never happen, left hand side should not be ragged */ + if (xhigh > xmax) xmax = xhigh; + bottom = -(item_no + 1) * item_height; + } + item = item->next; + } + } + + show_error(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); + wimp_get_window_state(&state); + + xorigin = state.xscroll - state.visible_area.xmin; + yorigin = state.yscroll - state.visible_area.ymax; + + _swix(OS_ReadModeVariable, _INR(0,1) | _OUT(2), -1, 11, &screenwidth); + _swix(OS_ReadModeVariable, _INR(0,1) | _OUT(2), -1, 12, &screenheight); + _swix(OS_ReadModeVariable, _INR(0,1) | _OUT(2), -1, 4, &tempint); + screenwidth <<= tempint; + _swix(OS_ReadModeVariable, _INR(0,1) | _OUT(2), -1, 5, &tempint); + screenheight <<= tempint; + + if (_kernel_osbyte(161, 28, 0) & (1<<(8+1))) /* solid drag */ + { + if (number_selected == 1) + { + box.dragging_box.xmin = xmin-xorigin; /* box shape */ + box.dragging_box.ymin = bottom-yorigin; + box.dragging_box.xmax = xmax-xorigin; + box.dragging_box.ymax = top-yorigin; + + redraw_params[0] = (int)hotlist_find_selected_item(); + redraw_params[1] = (int)item_height; + redraw_params[2] = (int)item_dir_width; + redraw_params[3] = (int)item_url_width; + + regs.r[0] = (2<<0) | (2<<2) | (1<<6) | (1<<7) | (1<<16); + regs.r[1] = (int)hotlist_drag_renderer; + regs.r[2] = (int)redraw_params; + regs.r[3] = (int)&(box.dragging_box); + + if (!_kernel_swi(DragAnObject_Start, ®s, ®s)) + { + hotlist_dragging = HOTLIST_SOLID_DRAG_OBJECT; /* Set global variable saying we are currently dragging */ + /* so we know whether to process a user_drag_box event */ + return NULL; + } + } + else + { + if (!read_sprite_size(SELECTION_SPRITE, &width, &height)) + { + box.dragging_box.xmin = pointerblock.x - (width/2 + 10); /* box shape */ + box.dragging_box.ymin = pointerblock.y - (height/2 + 10); + box.dragging_box.xmax = pointerblock.x + (width/2 + 10); + box.dragging_box.ymax = pointerblock.y + (height/2 + 10); + + regs.r[0] = (1<<0) | (1<<2) | (1<<6) | (1<<7); + regs.r[1] = (int)sprite_block; /* browser sprite area */ + regs.r[2] = (int)"package"; + regs.r[3] = (int)&(box.dragging_box); + + if (!_kernel_swi(DragASprite_Start, ®s, ®s)) + { + hotlist_dragging = HOTLIST_SOLID_DRAG_SPRITE; + return NULL; + } + } + } + } + box.drag_type = Wimp_DragBox_DragFixedDash; + box.dragging_box.xmin = xmin-xorigin; /* box shape */ + box.dragging_box.xmax = xmax-xorigin; + box.dragging_box.ymin = bottom-yorigin; + box.dragging_box.ymax = top-yorigin; + box.parent_box.xmin = -(pointerblock.x - box.dragging_box.xmin); /* Bounding box for dragged box */ + box.parent_box.xmax = screenwidth+(box.dragging_box.xmax - pointerblock.x); + box.parent_box.ymin = -(pointerblock.y - box.dragging_box.ymin); + box.parent_box.ymax = screenheight+(box.dragging_box.ymax - pointerblock.y); + err = wimp_drag_box(&box); /* start drag box */ + if (err) return err; + hotlist_dragging = HOTLIST_BOX_DRAG; /* Set global variable saying we are currently dragging */ + /* so we know whether to process a user_drag_box event */ + return NULL; +} + +void hotlist_start_drag(void) +{ + _kernel_oserror *err; + err = _hotlist_start_drag(); + if (err) + { + show_error_ret(err); + return; + } + /* Register NULL handler */ + register_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); +} + +/*************************************************/ +/* hotlist_is_inside() */ +/* */ +/* Checks if one hotlist_item is held inside */ +/* the directory structure of another */ +/* hotlist_item */ +/* */ +/* Parameters: pointer to possibly inside item */ +/* pointer to outside item */ +/* */ +/* Returns: 1 if item is inside, 0 otherwise */ +/*************************************************/ + +static int hotlist_is_inside(hotlist_item *inside, hotlist_item *outside) +{ + while(inside) + { + if (inside == outside) return 1; + inside = inside->parent; + } + return 0; +} + +/*************************************************/ +/* hotlist_drag_completed_handler() */ +/* */ +/* This function is called when a user_drag */ +/* completes. If the drag is one started by the */ +/* hotlist section it is processed. Dropping */ +/* the drag in the hotlist window will move or */ +/* copy the items being dragged. */ +/* Dropping the drag in any other window is */ +/* currently ignored */ +/*************************************************/ + +static int hotlist_drag_completed_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle) +{ + unsigned int item_height, item_dir_width, item_url_width; + ObjectId window_handle; + ComponentId comp; + WimpGetPointerInfoBlock pointerblock; + WimpGetWindowStateBlock state; + int winx, winy, shift; + unsigned int top, bottom, bottom2, tempint, position, xmin, xmax; + hotlist_item *targetitem, *sourceitem, *tempitem; + + _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); /* Check if SHIFT is pressed */ + + if (!hotlist_dragging) return 0; + + switch(hotlist_dragging) + { + case HOTLIST_SOLID_DRAG_OBJECT: + _swix(DragAnObject_Stop, 0); + break; + + case HOTLIST_SOLID_DRAG_SPRITE: + _swix(DragASprite_Stop, 0); + break; + + case HOTLIST_BOX_DRAG_SELECTION: + deregister_null_claimant(Wimp_ENull, hotlist_null_drag_select_handler, NULL); + return 0; /* No action taken at end of drag, selection is done by null handler during drag */ + break; + + default: + break; + } + + if (hotlist_current_highlighted) + { + hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + hotlist_current_highlighted = NULL; + } + + deregister_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); + + hotlist_dragging = 0; + wimp_get_pointer_info(&pointerblock); + + show_error(window_wimp_to_toolbox(0, + pointerblock.window_handle, + pointerblock.icon_handle, + &window_handle, + &comp)); + + if (window_handle == hotlist_windowid) + { + /* Drag was dropped in hotlist window */ + if (pointerblock.icon_handle != -1) return 0; /* Only understand drops on workspace */ + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + state.window_handle = pointerblock.window_handle; + wimp_get_window_state(&state); + winx = pointerblock.x + (state.xscroll - state.visible_area.xmin); + winy = pointerblock.y + (state.yscroll - state.visible_area.ymax); + targetitem = hotlist_find_item(hotlist_root->data.directory_content, -winy/item_height); + top = -winy/item_height; + + /* Decide where to put the items */ + if (targetitem) + { + hotlist_get_shape(&xmin, &xmax, targetitem); + if (targetitem->type == hl_directory && winx >= xmin && winx <= xmin+item_dir_width) + { + position = HOTLIST_POSITION_BEGINNING; /* Put in directory */ + } + else + { + if ((-winy % item_height) > item_height / 2) + position = HOTLIST_POSITION_AFTER; /* Put items after target item */ + else + position = HOTLIST_POSITION_BEFORE; /* Put items before target item */ + } + } + else + { + targetitem = hotlist_root; + position = HOTLIST_POSITION_END; + } + + tempint = hotlist_find_no_from_item(hotlist_find_selected_item()); + if (tempint < top) top = tempint; + bottom = hotlist_count_displayed_items(hotlist_root->data.directory_content); + + if (targetitem->flags & HOTLIST_G_IS_SELECTED) return 0; /* do nothing if dropped on selected items */ + + sourceitem = hotlist_find_selected_item(); + + /* Check to see if moving the selection to this position is acceptable -----------------------*/ + + tempitem = sourceitem; + while(tempitem) + { + if (tempitem->flags & HOTLIST_G_IS_SELECTED && tempitem->type == hl_directory) + { + if (hotlist_is_inside(targetitem, tempitem)) + { + StrNCpy0(erb.errmess, + lookup_token("NotIntoself:A directory cannot be copied or moved into itself.", + 0, + 0)); + erb.errnum = Utils_Error_Custom_Message; + show_error_ret(&erb); + return 0; + } + } + tempitem = tempitem->next; + } + + /* Only get this far if it is ----------------------------------------------------------------*/ + + sourceitem->flags &= ~HOTLIST_G_IS_SELECTED; + + if (!shift) + { + /* Move selected items */ + hotlist_move_item(sourceitem, targetitem, position); /* Move first item to specified position */ + /* before/after/in target */ + targetitem = sourceitem; + + while((sourceitem = hotlist_find_selected_item()) != NULL) + { + sourceitem->flags &= ~HOTLIST_G_IS_SELECTED; + hotlist_move_item(sourceitem, targetitem, HOTLIST_POSITION_AFTER); /* Move all subsequent items to follow */ + /* first moved item */ + targetitem = sourceitem; + } + } + else + { + /* Copy selected items */ + hotlist_copy_item(sourceitem, targetitem, position, &targetitem); /* Copy first item to specified position */ + /* before/after/in target */ + while((sourceitem = hotlist_find_selected_item()) != NULL) + { + sourceitem->flags &= ~HOTLIST_G_IS_SELECTED; + hotlist_copy_item(sourceitem, /* Copy all subsequent items to follow */ + targetitem, /* first copied item */ + HOTLIST_POSITION_AFTER, + &targetitem); + } + } + + number_selected = 0; + selected_parent = NULL; + + bottom2 = hotlist_count_displayed_items(hotlist_root->data.directory_content); + if (bottom2 > bottom) bottom = bottom2; + hotlist_redraw_items(top, bottom); + hotlist_window_preopen(already_open); + } + else + { + /* Drag was dropped in non-hotlist window */ + } + + return 0; +} + +void hotlist_autoscroll(int x, int y, int window_handle) +{ + int scroll_changed; + WimpGetWindowStateBlock state; + static unsigned int hscroll_speed, vscroll_speed; /* Separate h/v velocities */ + static int last_window_handle = 0; + + state.window_handle = window_handle; + + if (last_window_handle != window_handle) + { + last_window_handle = window_handle; + hscroll_speed = 0; + vscroll_speed = 0; + } + + wimp_get_window_state(&state); + + /* Auto scrolling of hotlist window when dragging */ + scroll_changed = 0; + if ((y > state.visible_area.ymax - HOTLIST_SCROLL_BOUNDARY_SIZE) && y < state.visible_area.ymax) + { + state.yscroll += vscroll_speed; + scroll_changed |= 1; + } + else + if ((y < state.visible_area.ymin + HOTLIST_SCROLL_BOUNDARY_SIZE) && y > state.visible_area.ymin) + { + state.yscroll -= vscroll_speed; + scroll_changed |= 1; + } + + if ((x > state.visible_area.xmax - HOTLIST_SCROLL_BOUNDARY_SIZE) && x < state.visible_area.xmax) + { + state.xscroll += hscroll_speed; + scroll_changed |= 2; + } + else + if ((x < state.visible_area.xmin + HOTLIST_SCROLL_BOUNDARY_SIZE) && x > state.visible_area.xmin) + { + state.xscroll -= hscroll_speed; + scroll_changed |= 2; + } + + if (scroll_changed) + { + wimp_open_window((WimpOpenWindowBlock*)&state); + } + if (scroll_changed & 1) + { + vscroll_speed += HOTLIST_SCROLL_SPEED_INC; + if (vscroll_speed > HOTLIST_SCROLL_SPEED_MAX) vscroll_speed = HOTLIST_SCROLL_SPEED_MAX; + } + else + { + vscroll_speed = HOTLIST_SCROLL_SPEED_MIN; + } + + if (scroll_changed & 2) + { + hscroll_speed += HOTLIST_SCROLL_SPEED_INC; + if (hscroll_speed > HOTLIST_SCROLL_SPEED_MAX) hscroll_speed = HOTLIST_SCROLL_SPEED_MAX; + } + else + { + hscroll_speed = HOTLIST_SCROLL_SPEED_MIN; + } +} + +/*************************************************/ +/* hotlist_null_handler() */ +/* */ +/* Called every null event while drag is in */ +/* operation */ +/*************************************************/ + +static int hotlist_null_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle) +{ + int x, y, buttons; + unsigned int itemno, xmin, xmax; + unsigned int item_height, item_dir_width, item_url_width; + hotlist_item *item; + ObjectId window; + ComponentId component; + WimpGetWindowStateBlock state; + + window_get_pointer_info(0, &x, &y, &buttons, &window, &component); + + if (window == hotlist_windowid) + { + window_get_wimp_handle(0, window, &state.window_handle); + + hotlist_autoscroll(x, y, state.window_handle); + + /* Display arrow pointing into directory to say that dropping */ + /* here will put in directory rather than next to it */ + wimp_get_window_state(&state); + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + x += (state.xscroll - state.visible_area.xmin); + y += (state.yscroll - state.visible_area.ymax); + + itemno = -y / item_height; + item = hotlist_find_item(hotlist_root->data.directory_content, itemno); + + if (item && item->type == hl_directory && !(item->flags & HOTLIST_G_IS_SELECTED)) + { + hotlist_get_shape(&xmin, &xmax, item); + if (x >= xmin && x <= xmin + item_dir_width) + { + if (hotlist_current_highlighted != item) + { + if (hotlist_current_highlighted) + { + hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + } + item->flags |= HOTLIST_D_IS_HIGHLIGHTED; + hotlist_current_highlighted = item; + highlighted_itemno = itemno; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + } + } + else + { + if (hotlist_current_highlighted) + { + hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + hotlist_current_highlighted = NULL; + } + } + } + else + { + if (hotlist_current_highlighted) + { + hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + hotlist_current_highlighted = NULL; + } + } + } + else + { + /* non hotlist window */ + if (hotlist_current_highlighted) + { + hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; + hotlist_redraw_items(highlighted_itemno, highlighted_itemno); + hotlist_current_highlighted = NULL; + } + } + return 0; +} + +/* Simple non-scrolling selection box, to be improved later */ + +int selection_x, selection_y; /* Workarea relative corner of selection box */ + +_kernel_oserror *hotlist_selection_box_start(void) +{ + _kernel_oserror *err; + WimpDragBox box; + WimpGetPointerInfoBlock pointerblock; + WimpGetWindowStateBlock state; + + wimp_get_pointer_info(&pointerblock); /* Read pointer position */ + + show_error(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); + wimp_get_window_state(&state); + + selection_x = pointerblock.x + (state.xscroll - state.visible_area.xmin); + selection_y = pointerblock.y + (state.yscroll - state.visible_area.ymax); + + box.drag_type = Wimp_DragBox_DragRubberDash; + box.dragging_box.xmin = pointerblock.x; /* box shape */ + box.dragging_box.xmax = pointerblock.x; + box.dragging_box.ymin = pointerblock.y; + box.dragging_box.ymax = pointerblock.y; + box.parent_box.xmin = state.visible_area.xmin; /* Bounding box for dragged box */ + box.parent_box.xmax = state.visible_area.xmax; + box.parent_box.ymin = state.visible_area.ymin; + box.parent_box.ymax = state.visible_area.ymax; + err = wimp_drag_box(&box); /* start drag box */ + if (err) return err; + hotlist_dragging = HOTLIST_BOX_DRAG_SELECTION; /* Set global variable saying we are currently dragging */ + /* so we know whether to process a user_drag_box event */ + + register_null_claimant(Wimp_ENull, hotlist_null_drag_select_handler, NULL); + return NULL; +} + +int hotlist_null_drag_select_handler(int event_code, WimpPollBlock *event, IdBlock *id_block, void *handle) +{ + WimpGetPointerInfoBlock pointerblock; + unsigned int item_height, item_dir_width, item_url_width; + int workx, worky; + hotlist_item *item; + WimpGetWindowStateBlock state; + unsigned int item_min, item_max, itemno, itemxmin, itemxmax; + int minx, maxx; + int last_item; + + wimp_get_pointer_info(&pointerblock); /* Read pointer position */ + + show_error(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); + wimp_get_window_state(&state); + + hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); + + workx = pointerblock.x + (state.xscroll - state.visible_area.xmin); + worky = pointerblock.y + (state.yscroll - state.visible_area.ymax); + + item_min = -worky / item_height; + if (-selection_y / item_height < item_min) item_min = -selection_y / item_height; + item_max = (-worky / item_height); + if ((-selection_y / item_height) > item_max) item_max = (-selection_y / item_height); + + if (selection_x < workx) + { + minx = selection_x; + maxx = workx; + } + else + { + minx = workx; + maxx = selection_x; + } + + /* Change from here onwards */ + + last_item = hotlist_count_displayed_items(hotlist_root->data.directory_content); + + for(itemno = 0; itemno <= last_item; itemno++) + { + item = hotlist_find_item(hotlist_root->data.directory_content, itemno); + if (item) + { + if (itemno >= item_min && itemno <= item_max) + { + hotlist_get_shape(&itemxmin, &itemxmax, item); + if (number_selected == 0) + { + if (!(maxx < itemxmin || minx > itemxmax) && (!(item->flags & HOTLIST_G_IS_SELECTED))) + { + item->flags |= HOTLIST_G_IS_SELECTED; + hotlist_redraw_items(itemno, itemno); + selected_parent = item->parent; + number_selected++; + } + } + else + { + if (!(maxx < itemxmin || minx > itemxmax)) + { + if ((selected_parent == item->parent || item->type == hl_url) && (!(item->flags & HOTLIST_G_IS_SELECTED))) + { + item->flags |= HOTLIST_G_IS_SELECTED; + hotlist_redraw_items(itemno, itemno); + number_selected++; + } + } + else + { + if (item->flags & HOTLIST_G_IS_SELECTED) + { + item->flags &= ~HOTLIST_G_IS_SELECTED; + hotlist_redraw_items(itemno, itemno); + number_selected--; + } + } + } + } + else + { + if (item->flags & HOTLIST_G_IS_SELECTED) + { + item->flags &= ~HOTLIST_G_IS_SELECTED; + hotlist_redraw_items(itemno, itemno); + number_selected--; + } + } + } + } + + return 0; +} diff --git a/h/Hotlist b/h/Hotlist new file mode 100644 index 0000000..8844d3e --- /dev/null +++ b/h/Hotlist @@ -0,0 +1,204 @@ +/* Copyright 1997 Acorn Computers Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/***************************************************/ +/* File : Hotlist.h */ +/* */ +/* Purpose: Managing a hotlist in the browser. */ +/* */ +/* Author : D.T.A.Brown */ +/* */ +/* History: 06-Aug-97: Created. */ +/* 22-Aug-97: (ADH/DTAB) Integrated into */ +/* main browser code. */ +/* 25-Aug-97: (ADH) Definitions imported */ +/* from TBEvents.h. */ +/***************************************************/ + +#ifndef REMOTE_HOTLIST + + /* Dialogue definitions (event codes) */ + + #define HotlistShowEditURL 0x300b + #define HotlistShowRenameDirectory 0x300c + #define HotlistShowNewURL 0x300d + #define HotlistShowNewDirectory 0x300e + + #define HotlistNewEditURLOk 0x300f + #define HotlistNewRenameDirectoryOk 0x3010 + #define HotlistNewEditURLCancel 0x3011 + #define HotlistNewRenameDirectoryCancel 0x3012 + + /* Menu definitions (event codes) */ + + #define ShowHotlist 0x3000 + #define AddThisPage 0x3001 + #define RemoveThisPage 0x3002 + #define SaveHotlist 0x3003 + + #define HotlistMenuOpened 0x3004 + #define HotlistMenuClosed 0x3005 + #define HotlistSelectAll 0x3006 + #define HotlistClearSelect 0x3007 + #define HotlistOpenAll 0x3008 + #define HotlistCloseAll 0x3009 + #define HotlistDelete 0x300a + + /* Enumerations */ + + typedef enum hotlist_type + { + hl_directory, + hl_url, + + /* Special case to allow range checking - must be the last item */ + + hl_ALL + + } hotlist_type; + + typedef enum hl_opentype + { + not_open, + already_open + + } hl_opentype; + + /* Structures */ + + typedef struct hotlist_item + { + hotlist_type type; /* Item type */ + unsigned int flags; + char * name; /* Leaf name (NULL terminated string) */ + + union + { + char * url; /* Pointer to url (NULL terminated string) */ + struct hotlist_item * directory_content; /* Pointer to directory contents */ + void * generic_data; /* generic type */ + + } data; + + struct hotlist_item * parent; /* Points to parent directory or NULL if none */ + struct hotlist_item * previous; /* Points to previous hotlist_item or NULL if none */ + struct hotlist_item * next; /* Points to next hotlist_item or NULL if no more */ + + } hotlist_item; + + /* Miscellaneous definitions */ + + #define HOTLIST_MAX_TYPE hl_url + + #define HOTLIST_G_IS_SELECTED (1<<0) + #define HOTLIST_G_REDRAW_NOW (1<<1) + + #define HOTLIST_D_HAS_SUBDIRECTORY (1<<16) + #define HOTLIST_D_IS_OPEN (1<<17) + #define HOTLIST_D_IS_HIGHLIGHTED (1<<18) + + #define DIRECTORY_FLAGS HOTLIST_G_REDRAW_NOW + + #define URL_FLAGS HOTLIST_G_REDRAW_NOW + + #define HOTLIST_SPRITE_ICON_FLAGS (WimpIcon_Sprite | \ + WimpIcon_HCentred | \ + WimpIcon_VCentred | \ + WimpIcon_Indirected | \ + WimpIcon_FGColour * 7 | \ + WimpIcon_BGColour * 0) + + #define HOTLIST_TEXT_ICON_FLAGS_SELECTED (WimpIcon_Text | \ + WimpIcon_Filled | \ + WimpIcon_VCentred | \ + WimpIcon_Indirected | \ + WimpIcon_FGColour * 1 | \ + WimpIcon_BGColour * 7) + + #define HOTLIST_TEXT_ICON_FLAGS_UNSELECTED (WimpIcon_Text | \ + WimpIcon_VCentred | \ + WimpIcon_Indirected | \ + WimpIcon_FGColour * 7 | \ + WimpIcon_BGColour * 0) + + #define HOTLIST_TEXT_ICON_FLAGS_DRAG (WimpIcon_Text | \ + WimpIcon_Filled | \ + WimpIcon_VCentred | \ + WimpIcon_Indirected | \ + WimpIcon_FGColour * 7 | \ + WimpIcon_BGColour * 1) + + #define HOTLIST_POSITION_BEFORE 0 /* before item */ + #define HOTLIST_POSITION_AFTER 1 /* after item */ + #define HOTLIST_POSITION_BEGINNING 2 /* beginning of directory */ + #define HOTLIST_POSITION_END 3 /* end of directory */ + + #define HOTLIST_BOX_DRAG 1 + #define HOTLIST_SOLID_DRAG_OBJECT 2 + #define HOTLIST_SOLID_DRAG_SPRITE 3 + #define HOTLIST_BOX_DRAG_SELECTION 4 + + #define HOTLIST_WINDOW_MIN_WIDTH 480 + #define HOTLIST_WINDOW_MIN_HEIGHT 3 /* In items */ + + #define HOTLIST_MENUSECTION_NEW 1 + #define HOTLIST_MENUSECTION_ALTER 2 + + #define OPEN_DIRECTORY_SPRITE "small_diro" + #define CLOSED_DIRECTORY_SPRITE "small_dir" + #define INSERT_DIRECTORY_SPRITE "small_dir+" + #define URL_SPRITE "small_b28" + #define SELECTION_SPRITE "package" + + #define HOTLIST_SCROLL_BOUNDARY_SIZE 32 + #define HOTLIST_SCROLL_SPEED_MIN 8 + #define HOTLIST_SCROLL_SPEED_INC 8 + #define HOTLIST_SCROLL_SPEED_MAX 128 + + /* Various component IDs */ + + #define HOTLIST_URL_MENUITEM 0x05 + #define HOTLIST_SELECTALL_MENUITEM 0x00 + #define HOTLIST_CLEARSELECTION_MENUITEM 0x01 + #define HOTLIST_NEWDIRECTORY_MENUITEM 0x02 + #define HOTLIST_NEWURL_MENUITEM 0x06 + #define HOTLIST_OPENALL_MENUITEM 0x03 + #define HOTLIST_CLOSEALL_MENUITEM 0x04 + + #define HOTLIST_SAVE_SUBMENUITEM 0x00 + #define HOTLIST_RENAME_SUBMENUITEM 0x01 + #define HOTLIST_DELETE_SUBMENUITEM 0x02 + + #define HOTLIST_NEWDIRECTORY_NAME 0x01 + #define HOTLIST_NEWDIRECTORY_CANCEL 0x03 + #define HOTLIST_NEWDIRECTORY_NEW 0x02 + + #define HOTLIST_NEWURL_NAME 0x01 + #define HOTLIST_NEWURL_URL 0x05 + #define HOTLIST_NEWURL_CANCEL 0x03 + #define HOTLIST_NEWURL_NEW 0x02 + + /* Function prototypes */ + + _kernel_oserror * hotlist_initialise (void); + + _kernel_oserror * hotlist_open (int show_type, void * type, int show_urls); + _kernel_oserror * hotlist_close (void); + + _kernel_oserror * hotlist_load (char * filename); + _kernel_oserror * hotlist_save (char * filename); + + _kernel_oserror * hotlist_add (char * description, char * url, int at_bottom); + +#endif -- GitLab