/* 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 "ChoiceDefs.h" #include "FetchPage.h" #include "Filetypes.h" #include "Menus.h" #include "Mouse.h" #include "Multiuser.h" #include "Protocols.h" #include "Save.h" #include "Toolbars.h" #include "URLUtils.h" #include "Windows.h" #include "Hotlist.h" /* Internationalisation support */ #ifdef UNIFONT #define CHARSET_SPECIFIER "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" #else #define CHARSET_SPECIFIER "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">\n" #endif /* Local definitions */ #define HotlistWrite(fn) {written = (fn); if (written < 0) goto hotlist_save_error;} /* Local statics */ static int autoopen_oldtime; /* Base time for autoopen directory */ static hotlist_item * hotlist_root = NULL; /* Pointer to the hotlist root directory item */ static int hotlist_windowid = NULL_ObjectId; /* 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 int menu_select = 0; /* 1 if an item had to be selected when the menu was opened */ static unsigned int last_selected_item = 0xffffffff; /* The last item which was selected */ 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 int hl_show_urls; /* 0 when url descriptions to be shown, 1 to show urls */ 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 */ /* Local structures */ typedef struct hl_dragging_details { char drag_type; /* One of HOTIST_NOT_DRAGGING, HOTLIST_BOX_DRAG, HOTLIST_SOLID_DRAG_OBJECT, */ /* HOTLIST_SOLID_DRAG_SPRITE, or HOTLIST_BOX_DRAG_SELECTION - See Hotlist.h */ unsigned int using_adjust :1; } hl_dragging_details; static hl_dragging_details hotlist_dragging; /* Event handler prototypes */ // Write these out in full, to clearly state exactly what parameters are needed? // Or is this in fact more robust, as the compiler will fault mismatched functions // in this source as opposed to wherever a registration of the handler occurs? 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_select_all_handler; static ToolboxEventHandler hotlist_clear_selection_handler; static ToolboxEventHandler hotlist_menu_openall_handler; static ToolboxEventHandler hotlist_menu_closeall_handler; static ToolboxEventHandler hotlist_menu_delete_handler; static ToolboxEventHandler hotlist_save_to_server_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; static ToolboxEventHandler hotlist_show_descriptions_handler; static ToolboxEventHandler hotlist_show_urls_handler; static ToolboxEventHandler hotlist_drag_stop_handler; static ToolboxEventHandler hotlist_close_handler; /* Debug functions */ #ifdef TRACE static void hotlist_display_item(hotlist_item * item); static void hotlist_display_tree(hotlist_item * list, int indent); #endif /* List enquiry and manupulation */ static _kernel_oserror * hotlist_link (hotlist_item * item, hotlist_item * target, unsigned int position); static void hotlist_unlink (hotlist_item * item); 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_desc, 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); /* Item enquiry and manipulation */ static _kernel_oserror * hotlist_get_entry_sizes (unsigned int * item_height, unsigned int * item_dir_width, unsigned int * item_url_width); 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); /* Finding items */ static hotlist_item * hotlist_find_item (hotlist_item * list, unsigned int item_no); static hotlist_item * hotlist_find_item_r (hotlist_item * list, unsigned int item_no, int * curr_item); static int hotlist_find_no_from_item (hotlist_item * item); static int hotlist_find_no_from_item_r (hotlist_item * list, hotlist_item * item, int * curr_item); static hotlist_item * hotlist_find_selected_item_r (hotlist_item * list); /* Counting items */ static void hotlist_count_selected_items_r (hotlist_item * list, int * count); static unsigned int hotlist_count_displayed_items (hotlist_item * list); static void hotlist_count_displayed_items_r (hotlist_item * list, int * count); // Categorisation work in progress... static _kernel_oserror * hotlist_draw (hotlist_item * list, unsigned int first_item, unsigned int last_item); static _kernel_oserror * hotlist_draw_r (hotlist_item *list, unsigned int first_item, unsigned int last_item, int * curr_item, unsigned int indent, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width); static unsigned int hotlist_contents_selected (hotlist_item * item); static unsigned int hotlist_no_contents_selected (hotlist_item * item); static unsigned int hotlist_get_max_width (hotlist_item * list); static _kernel_oserror * hotlist_get_max_width_r (hotlist_item *list, unsigned int indent, int * max_width, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width); static _kernel_oserror * hotlist_redraw_now (void); static _kernel_oserror * hotlist_redraw_now_r (hotlist_item * list, int * curr_item); static _kernel_oserror * hotlist_get_shape (int * xmin, int * xmax, hotlist_item * item); static _kernel_oserror * hotlist_directory_open_close (hotlist_item * item, unsigned int itemno); static _kernel_oserror * hotlist_redraw_items (unsigned int firstitem, unsigned int lastitem); static _kernel_oserror * 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 int hotlist_preopen (void); static _kernel_oserror * hotlist_set_menu_details (ObjectId menuid); static _kernel_oserror * hotlist_save_entries (FILE * fileptr, hotlist_item * list, int type, int save_read_only); static void hotlist_lower_tags (char * string); static void hotlist_drag_renderer (hotlist_item * item, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width); static _kernel_oserror * hotlist_start_drag (void); static _kernel_oserror * hotlist_start_drag_backend (void); static _kernel_oserror * hotlist_modified (unsigned int type); static void hotlist_convert_drag_selection (hotlist_item * item); static _kernel_oserror * hotlist_autoscroll (int window); static _kernel_oserror * hotlist_load_directory (FILE * fileptr, hotlist_item * target); static int hotlist_get_selected_shape (BBox * box); static _kernel_oserror * hotlist_get_selected_shape_r (hotlist_item * list, BBox * box, unsigned int * itemno, int * found, unsigned int item_height); static _kernel_oserror * hotlist_select_box (unsigned int first_item, unsigned int last_item, int minx, int maxx); static _kernel_oserror * hotlist_select_box_r (unsigned int first_item, unsigned int last_item, hotlist_item *item, unsigned int *itemno, int minx, int maxx); static void hotlist_find_match_r (char * buffer, int buffer_size, hotlist_item * item, hotlist_item ** lowest_item, int * lowest_offset, int * lowest_diff); /* Save Protocol */ static _kernel_oserror * hotlist_initiate_uri_save (hotlist_item *sourceitem); static _kernel_oserror * hotlist_initiate_html_save (char *filename); #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_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_is_inside() */ // /* */ // /* Checks if one hotlist_item is held inside */ // /* the directory structure of another */ // /* hotlist_item */ // /* */ // /* Parameters: Pointer to the hotlist_item that */ // /* may be inside the parent; */ // /* */ // /* Pointer to the parent */ // /* hotlist_item. */ // /* */ // /* Returns: 1 if item is inside, else 0. */ // /*************************************************/ // // static int hotlist_is_inside(hotlist_item * inside, hotlist_item * outside) // { // while (inside) // { // if (inside == outside) return 1; // inside = inside->parent; // } // // return 0; // } /*************************************************/ /* 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_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_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; int is_untitled = 0; if (!directory_name) is_untitled = 1; else { while (*directory_name && *directory_name <= ' ') directory_name ++; if (!*directory_name) is_untitled = 1; else { int len = strlen(directory_name); while (len && directory_name[len - 1] <= ' ') len--; if (!len) is_untitled = 1; else directory_name[len] = '\0'; } } if (is_untitled) directory_name = lookup_token("HotlistUntitled:(Untitled)",0,0); /* Allocate space for the item and its name */ item = malloc(sizeof(hotlist_item)); if (!item) return make_no_memory_error(4); perm_dirname = malloc(strlen(directory_name) + 1); if (!perm_dirname) { free(item); return make_no_memory_error(5); } /* 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_desc, char * url) { char * perm_url_desc; char * perm_url; hotlist_item * item; _kernel_oserror * e; /* Do we have a URL string? */ if (!url) return NULL; while (*url && *url <= ' ') url++; if (!*url) return NULL; /* Do we have a useful title string? */ if (!url_desc) url_desc = url; else { while (*url_desc && *url_desc <= ' ') url_desc ++; if (!*url_desc) url_desc = url; else { int len = strlen(url_desc) - 1; while (len && url_desc[len] <= ' ') len--; if (!len) url_desc = url; else url_desc[len + 1] = '\0'; } } /* Allocate a new hotlist_item structure */ if ((item = malloc(sizeof(hotlist_item))) == NULL) { #ifdef TRACE if (tl & (1<<25)) Printf("hotlist_new_url: 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_desc) + 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_desc); 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); } 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) { hotlist_item * newdir, * tempptr; /* If the item is read only, don't move it; copy instead. */ if (source->flags & HOTLIST_G_IS_READ_ONLY) { return hotlist_copy_item(source, target, position, NULL); } /* Otherwise, moving it is OK */ if (!(source->type == hl_directory && !hotlist_contents_selected(source->data.directory_content))) { /* Unlink item from directory structure */ hotlist_unlink(source); /* Link into new position in directory structure */ return hotlist_link(source, target, position); } else { /* Special case - moving a directory whose contents are only partially selected. */ /* We can't move a directory whose contents are only partially to be moved, */ /* there would be nowhere to leave the items which were not moved with it. */ /* So, create a new directory based on the old */ RetError(hotlist_new_directory(target, source->name, position, &newdir)); /* Move the contents recursively into the new directory */ source = source->data.directory_content; while (source) { tempptr = source->next; if (source->flags & HOTLIST_G_IS_SELECTED) { hotlist_move_item(source, newdir, HOTLIST_POSITION_END); source->flags &= ~HOTLIST_G_IS_SELECTED; } source = tempptr; } } return NULL; } /*************************************************/ /* 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) { hotlist_item * newdir; newdir = NULL; switch(source->type) { /* For a URL, create a new item based on the source one */ case hl_url: { RetError(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) { if (content->flags & HOTLIST_G_IS_SELECTED) { 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_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) { 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; if (item_dir_width) *item_dir_width += 8; if (item_height) *item_height += 8; return NULL; } /*************************************************/ /* hotlist_set_flags() */ /* */ /* This function will recursively set flags for */ /* either a specified type of hotlist_item or */ /* all hotlist_items, to the given value. All */ /* items which are changed will have their */ /* HOTLIST_G_REDRAW_NOW bit set. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start on; */ /* */ /* Type of hotlist_item to set flags */ /* for, or hl_ALL for all types; */ /* */ /* Flags to set. */ /* */ /* Returns: 1 if any flags were set, else 0. */ /*************************************************/ static int hotlist_set_flags(hotlist_item * list, hotlist_type type, unsigned int flags) { int changed = 0; while (list) { /* Alter all items, or those of the correct type */ if (type == hl_ALL || type == list->type) { if (list->flags | flags != list->flags) { list->flags |= HOTLIST_G_REDRAW_NOW; changed = 1; } list->flags |= flags; } /* Recursive call for directories */ 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 recursively clear flags */ /* for either a specified type of hotlist_item */ /* or all hotlist_items. All items changed will */ /* have their HOTLIST_G_REDRAW_NOW bit set, */ /* unless, of course, the routine is called to */ /* clear that bit. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start on; */ /* */ /* Type of hotlist_item to set flags */ /* for, or hl_ALL for all types; */ /* */ /* Flags clear word (any bits set in */ /* this word are cleared in the item */ /* flags). */ /* */ /* Returns: 1 if flags were cleared, else 0. */ /*************************************************/ static int hotlist_clear_flags(hotlist_item * list, hotlist_type type, unsigned int flags) { int changed = 0; while (list) { /* Only alter the requested item types */ if (type == hl_ALL || type == list->type) { if ((list->flags & ~flags) != list->flags) { list->flags |= HOTLIST_G_REDRAW_NOW; changed = 1; } list->flags &= ~flags; } /* Recursive call for directories */ if (list->type == hl_directory) { if (hotlist_clear_flags(list->data.directory_content, type, flags)) changed = 1; } list = list->next; } return changed; } /*************************************************/ /* 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 at the */ /* top of the directory to scan; */ /* */ /* The nth item to return within it. */ /* */ /* 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) { int curr_item = 0; return hotlist_find_item_r(list, item_no, &curr_item); } /*************************************************/ /* hotlist_find_item_r() */ /* */ /* Recursive back-end to hotlist_find_item. */ /* */ /* Parameters: Pointer to a hotlist_item at the */ /* top of the directory to scan; */ /* */ /* The nth item to return within it; */ /* */ /* Pointer to an int, in which the */ /* number of the current item is */ /* accumulated (initialised to an */ /* appropriate value, usually 0). */ /* */ /* Returns: As hotlist_find_item. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static hotlist_item * hotlist_find_item_r(hotlist_item * list, unsigned int item_no, int * curr_item) { hotlist_item * temp; while (list) { if (*curr_item == item_no) return list; /* Found the list item */ /* Increment the item counter */ *curr_item += 1; /* Recursively scan open directories */ if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { temp = hotlist_find_item_r(list->data.directory_content, item_no, curr_item); if (temp) return temp; } /* Move to the next list item */ list = list->next; } return NULL; /* List does not extend far enough */ } /*************************************************/ /* 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 to */ /* start at; */ /* */ /* Pointer to the item whos position */ /* is to be returned. */ /* */ /* Returns: The item position or -1 if it is */ /* not found. */ /*************************************************/ static int hotlist_find_no_from_item(hotlist_item * item) { int curr_item = 0; return hotlist_find_no_from_item_r(hotlist_root->data.directory_content, item, &curr_item); } /*************************************************/ /* hotlist_find_no_from_item_r() */ /* */ /* Recursive back-end to */ /* hotlist_find_no_from_item. */ /* */ /* Parameters: Pointer to a hotlist_item to */ /* start at; */ /* */ /* Pointer to the item whos position */ /* is to be returned; */ /* */ /* Pointer to an int, in which the */ /* number of the current item is */ /* accumulated (initialised to an */ /* appropriate value, usually 0). */ /* */ /* Returns: As hotlist_find_no_from_item. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static int hotlist_find_no_from_item_r(hotlist_item * list, hotlist_item * item, int * curr_item) { /* Start the search at the given item */ while (list) { if (item == list) return *curr_item; /* Found the list item */ *curr_item += 1; /* Recursively scan open directories */ if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { int found; found = hotlist_find_no_from_item_r(list->data.directory_content, item, curr_item); if (found >= 0) return found; } /* Move to the next item */ list = list->next; } /* Didn't find it */ return -1; } /*************************************************/ /* hotlist_find_selected_item() */ /* */ /* This function returns a pointer to the first */ /* selected item found. */ /* */ /* Returns: Pointer to a hotlist_item struct */ /* which was the first selected item */ /* found in a search starting */ /* from the root, or NULL if there */ /* is nothing selected. */ /*************************************************/ hotlist_item * hotlist_find_selected_item(void) { return hotlist_find_selected_item_r(hotlist_root); } /*************************************************/ /* hotlist_find_selected_item_r() */ /* */ /* Recursive back-end to */ /* hotlist_find_selected_item. */ /* */ /* Parameters: Pointer to a hotlist_item */ /* struct to start at. */ /* */ /* Returns: As hotlist_find_selected_item. */ /*************************************************/ static hotlist_item * hotlist_find_selected_item_r(hotlist_item * list) { while (list) { if (list->flags & HOTLIST_G_IS_SELECTED) return list; /* Found it */ /* Recursively scan all directories */ if (list->type == hl_directory) { hotlist_item * found; found = hotlist_find_selected_item_r(list->data.directory_content); if (found) return found; } /* Move to the next item */ list = list->next; } /* Didn't find it */ return NULL; } /*************************************************/ /* hotlist_count_selected_items() */ /* */ /* Count the number of items that are currently */ /* selected in the hotlist window. */ /* */ /* If you want to know if all items are selected */ /* use hotlist_contents_selected. If you want to */ /* know if no items are selected, it is faster */ /* to use hotlist_no_contents_selected than */ /* compare the return value of this function */ /* against zero (this function *must* scan all */ /* hotlist items, whereas the other can exit as */ /* soon as a selected item is found). */ /* */ /* Returns: The number of selected items. */ /*************************************************/ unsigned int hotlist_count_selected_items(void) { int count = 0; hotlist_count_selected_items_r(hotlist_root->data.directory_content, &count); return count; } /*************************************************/ /* hotlist_count_selected_items_r() */ /* */ /* Recursive back-end to */ /* hotlist_count_selected_items. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* representing the first item in a */ /* directory to count; */ /* */ /* Pointer to an int, in which the */ /* total is accumulated. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static void hotlist_count_selected_items_r(hotlist_item * list, int * count) { while (list) { if (list->flags & HOTLIST_G_IS_SELECTED) { *count += 1; } else { /* Only recurse through directories which are not selected */ if (list->type == hl_directory) { hotlist_count_selected_items_r(list->data.directory_content, count); } } list = list->next; } } /*************************************************/ /* hotlist_count_displayed_items() */ /* */ /* This routine counts the number of items */ /* displayed in the hotlist window. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start at. */ /* */ /* Returns: The number of displayed items. */ /*************************************************/ static unsigned int hotlist_count_displayed_items(hotlist_item * list) { int count = 0; hotlist_count_displayed_items_r(list, &count); return count; } /*************************************************/ /* hotlist_count_displayed_items_r() */ /* */ /* Recursive back-end to */ /* hotlist_count_displayed_items. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start at; */ /* */ /* Pointer to an int, in which the */ /* total is accumulated. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static void hotlist_count_displayed_items_r(hotlist_item *list, int * count) { while (list) { /* Recursive scan for open directories */ if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { hotlist_count_displayed_items_r(list->data.directory_content, count); } list = list->next; *count += 1; } } /*************************************************/ /* hotlist_draw() */ /* */ /* Redraws a specified region of the hotlist. */ /* Assumes graphics rectangles are set up */ /* appropriately (e.g. the function is called */ /* during a Wimp redraw session). */ /* */ /* Parameters: Pointer to a hotlist_item giving */ /* the list that we're to draw; */ /* */ /* First item number to draw (count */ /* the visible items from the top of */ /* the window downwards starting at */ /* an item number of zero); */ /* */ /* Last item number to draw. */ /*************************************************/ static _kernel_oserror * hotlist_draw(hotlist_item * list, unsigned int first_item, unsigned int last_item) { int curr_item = 0; unsigned int item_height, item_dir_width, item_url_width; RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); return hotlist_draw_r(list, first_item, last_item, &curr_item, 0, item_height, item_dir_width, item_url_width); } /*************************************************/ /* hotlist_draw_r() */ /* */ /* Recursive back-end to hotlist_draw. */ /* */ /* Parameters: Pointer to a hotlist_item giving */ /* the list that we're to draw; */ /* */ /* First item number to draw (count */ /* the visible items from the top of */ /* the window downwards starting at */ /* an item number of zero); */ /* */ /* Last item number to draw; */ /* */ /* Pointer to an int, which is used */ /* to accumulate the current item */ /* number - the contents should be */ /* initialised to an appropriate */ /* value for the first three */ /* parameters (usually, zero); */ /* */ /* Level of indentation (OS units); */ /* */ /* Height of an item (OS units); */ /* */ /* Width of a directory item (OS */ /* units); */ /* */ /* Width of a URL item (OS units). */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static _kernel_oserror * hotlist_draw_r(hotlist_item *list, unsigned int first_item, unsigned int last_item, int * curr_item, unsigned int indent, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width) { WimpIconBlock icon; unsigned int temp_width; int text_width; while (list) { /* Break out if done all items to be displayed */ if (*curr_item > last_item) return NULL; /* Don't draw until we reach the required first item */ if (*curr_item >= first_item) { /* Construct an icon block for Wimp_PlotIcon. First, icon flags. */ icon.flags = HOTLIST_SPRITE_ICON_FLAGS; if (((list->flags & HOTLIST_G_IS_SELECTED) ? 1 : 0) ^ ((list->flags & HOTLIST_G_DRAG_SELECTED) ? 1 : 0) ) icon.flags |= WimpIcon_Selected; /* Point to the main sprite pool */ icon.data.is.sprite_area = (void*) sprite_block; /* Set type-dependent characteristics */ switch(list->type) { case hl_url: { /* A URL item; set the sprite name and item width accordingly */ icon.data.is.sprite = !(list->flags & HOTLIST_G_IS_READ_ONLY) ? URL_SPRITE : RESOURCES_URL_SPRITE; icon.data.is.sprite_name_length = strlen(URL_SPRITE); temp_width = item_url_width; } break; case hl_directory: { /* Directories are a bit more complex, as they can be open, */ /* closed, or showing a '+' if a dragged item is hovering */ /* over it and would drop 'into' the directory if released. */ temp_width = item_dir_width; /* Width is based on the widest sprite of the three */ if (list->flags & HOTLIST_D_IS_HIGHLIGHTED) { /* The item is either selected (something is 'hovering' over it)... */ icon.data.is.sprite = INSERT_DIRECTORY_SPRITE; icon.data.is.sprite_name_length = strlen(INSERT_DIRECTORY_SPRITE); } else { /* ...or unselected. In that case it is either open or closed. */ if (list->flags & HOTLIST_D_IS_OPEN) { icon.data.is.sprite = !(list->flags & HOTLIST_G_IS_READ_ONLY) ? OPEN_DIRECTORY_SPRITE : OPEN_RESOURCES_DIRECTORY_SPRITE; } else { icon.data.is.sprite = !(list->flags & HOTLIST_G_IS_READ_ONLY) ? CLOSED_DIRECTORY_SPRITE : CLOSED_RESOURCES_DIRECTORY_SPRITE; } icon.data.is.sprite_name_length = strlen(icon.data.is.sprite); } } break; default: { temp_width = 0; /* Should never happen... */ } break; } /* Set the item bounding box appropriately */ icon.bbox.xmin = indent; icon.bbox.xmax = indent + temp_width; icon.bbox.ymin = -item_height * (*curr_item) - item_height; icon.bbox.ymax = -item_height * (*curr_item); /* Plot the item */ RetError(wimp_plot_icon(&icon)); /* We now need to plot the item text. First, get the width, */ /* taking the opportunity to point the icon that we'll plot */ /* to the appropriate text, too. */ if (list->type == hl_url && hl_show_urls) { /* If this is a URL item and we are to show URLs, then find */ /* the width of the URL rather than the description. */ RetError(utils_text_width(list->data.url, &text_width, 0)); /* Point to the URL text */ icon.data.it.buffer = list->data.url; icon.data.it.buffer_size = strlen(list->data.url); icon.data.it.validation = NULL; } else { /* Otherwise (any other item, or a URL item when we're showing */ /* descriptions) just find the name's width. */ RetError(utils_text_width(list->name, &text_width, 0)); /* Point to the description */ icon.data.it.buffer = list->name; icon.data.it.buffer_size = strlen(list->name); icon.data.it.validation = NULL; } /* Set the bounding box for the text. The hard coded */ /* constants are nothing critical - just aesthetics. */ icon.bbox.xmin = indent + temp_width + 2; icon.bbox.xmax = indent + temp_width + 2 + text_width + 12; icon.bbox.ymin = -item_height * (*curr_item) - item_height + 2; icon.bbox.ymax = -item_height * (*curr_item) - 2; /* Set the flags accordingly if the text is selected or unselected */ if (((list->flags & HOTLIST_G_IS_SELECTED) ? 1 : 0) ^ ((list->flags & HOTLIST_G_DRAG_SELECTED) ? 1 : 0)) icon.flags = HOTLIST_TEXT_ICON_FLAGS_SELECTED; else icon.flags = HOTLIST_TEXT_ICON_FLAGS_UNSELECTED; /* Finally, plot the item. */ RetError(wimp_plot_icon(&icon)); } /* Increment the item number */ *curr_item += 1; /* Recursive redraw for directories */ if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { RetError(hotlist_draw_r(list->data.directory_content, first_item, last_item, curr_item, indent + item_dir_width, item_height, item_dir_width, item_url_width)); } /* Move on down the list */ list = list->next; } return NULL; } /*************************************************/ /* hotlist_get_max_width_r */ /* */ /* Recursive back-end to hotlist_get_max_width. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start at; */ /* */ /* An indent in OS units, as for */ /* redrawing the hotlist; */ /* */ /* Pointer to an int, in which the */ /* width of the widest item so far */ /* is accumulated; */ /* */ /* Height of a hotlist item in OS */ /* units; */ /* */ /* Width of a hotlist directory */ /* sprite in OS units; */ /* */ /* Width of a hotlist URL sprite in */ /* OS units. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static _kernel_oserror * hotlist_get_max_width_r(hotlist_item *list, unsigned int indent, int * max_width, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width) { _kernel_oserror * e; unsigned int item_width; int text_width; while (list) { /* Get width of the icon */ switch (list->type) { case hl_directory: item_width = item_dir_width; break; case hl_url: item_width = item_url_width; break; default: item_width = 0; break; } /* Work out width of the text */ if (list->type == hl_url && hl_show_urls) e = utils_text_width(list->data.url, &text_width, 0); else e = utils_text_width(list->name, &text_width, 0); if (e) return e; /* Account for the indent and spacers (aesthetics) */ item_width += indent + 2 + text_width + 12; /* If this is wider than so far recorded, store the new value */ if (item_width > *max_width) *max_width = item_width; /* Recursive call for open directories */ if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { RetError(hotlist_get_max_width_r(list->data.directory_content, indent + item_dir_width, max_width, item_height, item_dir_width, item_url_width)); } /* Move on down the list */ list = list->next; } return NULL; } /*************************************************/ /* hotlist_get_max_width() */ /* */ /* This function returns the maximum width of */ /* of the displayed hotlist entries. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start at. This is assumed to */ /* be at zero indent from the left */ /* hand side. */ /* */ /* Returns: Of all visible entries, the width */ /* of the widest, in OS units. */ /*************************************************/ static unsigned int hotlist_get_max_width(hotlist_item * list) { unsigned int item_height, item_dir_width, item_url_width; int widest = 0; /* Find basic item size information */ if (hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)) return 0; /* Scan the directory and all open directories with in it for */ /* the widest item */ if (hotlist_get_max_width_r(list, 0, &widest, item_height, item_dir_width, item_url_width)) return 0; /* Return the result */ return widest; } /*************************************************/ /* hotlist_redraw_now() */ /* */ /* This function redraws all visible items with */ /* the HOTLIST_G_REDRAW_NOW bit set. */ /*************************************************/ static _kernel_oserror * hotlist_redraw_now(void) { int curr_item = 0; return hotlist_redraw_now_r(hotlist_root->data.directory_content, &curr_item); } /*************************************************/ /* hotlist_redraw_now_r() */ /* */ /* Recursive back-end to hotlist_redraw_now. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* to start on; */ /* */ /* Pointer to an int, in which the */ /* current item number being redrawn */ /* is accumulated. */ /* */ /* Assumes: The pointer to the int may *not* */ /* be NULL. */ /*************************************************/ static _kernel_oserror * hotlist_redraw_now_r(hotlist_item * list, int * curr_item) { while (list) { if (list->flags & HOTLIST_G_REDRAW_NOW) { /* Redraw just the one item */ RetError(hotlist_redraw_items(*curr_item, *curr_item)); /* Clear the flag */ list->flags &= ~HOTLIST_G_REDRAW_NOW; } *curr_item += 1; if (list->type == hl_directory && list->flags & HOTLIST_D_IS_OPEN) { /* Recursive call for open directories */ RetError(hotlist_redraw_now_r(list->data.directory_content, curr_item)); } list = list->next; } return NULL; } /*************************************************/ /* 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; hotlist_item * target; int type; target = hotlist_find_selected_item(); if (!target) { position = at_bottom ? HOTLIST_POSITION_END : HOTLIST_POSITION_BEGINNING; target = hotlist_root; type = 1; /* If adding to the beginning of the hotlist, skip any */ /* read-only items at the top first. */ if (position == HOTLIST_POSITION_BEGINNING) { hotlist_item * check = target->data.directory_content; /* Look for something not read-only */ while (check && check->next) { if (check->next->flags & HOTLIST_G_IS_READ_ONLY) check = check->next; else break; } if (check && (check->flags & HOTLIST_G_IS_READ_ONLY)) { position = HOTLIST_POSITION_AFTER; target = check; } } } else { if (target->type == hl_directory) { position = HOTLIST_POSITION_BEGINNING; } else { position = HOTLIST_POSITION_AFTER; } type = 2; } /* Add the item and ensure the window extent etc. is correct */ e = hotlist_new_url(target, position, description, url); if (!e) hotlist_preopen(); /* 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: { if (type == 1) { hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content)); } else { if (target->flags & HOTLIST_D_IS_OPEN) { hotlist_redraw_items(hotlist_find_no_from_item(target), hotlist_count_displayed_items(hotlist_root->data.directory_content)); } } } break; case HOTLIST_POSITION_BEFORE: case HOTLIST_POSITION_AFTER: { hotlist_redraw_items(hotlist_find_no_from_item(hotlist_newitem), hotlist_count_displayed_items(hotlist_root->data.directory_content)); } } if (!e) return hotlist_modified(HL_MODIFIED_ADD); return e; } /*************************************************/ /* hotlist_get_shape() */ /* */ /* This function calculates the xmin and xmax */ /* of the passed hotlist_item. */ /* */ /* Parameters: Pointer to an int, in which the */ /* xmin offset (in OS units) from */ /* the left hand edge is returned; */ /* */ /* Similarly, a pointer to an int to */ /* take the xmax offset; */ /* */ /* Pointer to the hotlist_item to */ /* examine. */ /* */ /* Assumes: Any pointer may be NULL. */ /*************************************************/ static _kernel_oserror * hotlist_get_shape(int * xmin, int * xmax, hotlist_item * item) { hotlist_item * tempitem; unsigned int item_height, item_dir_width, item_url_width; int icon_width; int text_width; int count = 0; if (xmin) *xmin = 0; if (xmax) *xmax = 0; if (!item) return NULL; /* Find entry sizes */ RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* Count how many parent items we have. This allows */ /* the left hand indent to be calculated. */ tempitem = item; while (tempitem) { tempitem = tempitem->parent; count++; } count -= 2; /* Find the text width */ if (item->type == hl_url && hl_show_urls) utils_text_width(item->data.url, &text_width, 0); else utils_text_width(item->name, &text_width, 0); /* Add in the sprite width */ switch (item->type) { case hl_directory: icon_width = item_dir_width; break; case hl_url: icon_width = item_url_width; break; default: icon_width = 0; break; } /* Add up the total and exit */ if (xmin) *xmin = count * item_dir_width; if (xmax) *xmax = count * item_dir_width + icon_width + 2 + text_width + 12; return NULL; } /*************************************************/ /* hotlist_directory_open_close() */ /* */ /* This function opens or closes a directory, */ /* dealing with all required redrawing. */ /* */ /* Parameters: Pointer to the hotlist_item */ /* struct representing the directory */ /* to open or close; */ /* */ /* Number of that item (counting the */ /* visible hotlist items from the */ /* top of the window downwards, */ /* starting at zero). */ /*************************************************/ static _kernel_oserror * 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; RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* Swap the flag saying whether the directory is open or not */ item->flags ^= HOTLIST_D_IS_OPEN; /* Clear all selected items within the directory */ hotlist_clear_flags(item->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED | HOTLIST_G_REDRAW_NOW); /* Do the appropriate redrawing */ hotlist_preopen(); RetError(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); RetError(window_get_extent(0, hotlist_windowid, &bbox)); top = -itemno * item_height; if (item->data.directory_content) { RetError(wimp_force_redraw(window_handle, bbox.xmin, bbox.ymin, bbox.xmax, top)); } else { RetError(wimp_force_redraw(window_handle, bbox.xmin, top-item_height, bbox.xmax, top)); } return NULL; } /*************************************************/ /* hotlist_redraw_items() */ /* */ /* This function forces the redraw of a set of */ /* items. */ /* */ /* Parameters: Item number of the first item to */ /* redraw (counting the visible */ /* items from the top of the window */ /* downwards, starting at zero) - */ /* this is inclusive; */ /* */ /* Item number of the last item to */ /* redraw (also inclusive). */ /*************************************************/ static _kernel_oserror * hotlist_redraw_items(unsigned int firstitem, unsigned int lastitem) { unsigned int item_height, item_dir_width, item_url_width; BBox bbox; int window_handle; /* Get the entry sizes */ RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* Find the window extent */ RetError(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); RetError(window_get_extent(0, hotlist_windowid, &bbox)); /* Force a redraw for the whole extent width, over a vertical */ /* span determined by the given item numbers. */ return wimp_force_redraw(window_handle, bbox.xmin, - (lastitem + 1) * item_height, bbox.xmax, - firstitem * item_height); } /*************************************************/ /* hotlist_clear_selection() */ /* */ /* This function unselects all items, redrawing */ /* as required. */ /*************************************************/ _kernel_oserror * hotlist_clear_selection(void) { if ( hotlist_clear_flags(hotlist_root->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED) ) return hotlist_redraw_now(); return NULL; } /*************************************************/ /* hotlist_launch_url() */ /* */ /* This function launches the url in the passed */ /* item. */ /* */ /* Parameters: Pointer to a hotlist_item struct */ /* holding the URL to launch. */ /*************************************************/ static _kernel_oserror * hotlist_launch_url(hotlist_item * item) { #ifdef TRACE if (tl & (1u<<25)) hotlist_display_item(item); #endif return windows_create_browser(item->data.url, NULL, NULL, NULL, Windows_CreateBrowser_Normal); } /*************************************************/ /* hotlist_process_click_on_item() */ /* */ /* Deal with clicks on hotlist items. */ /* */ /* Parameters: Item number that was clicked upon */ /* (counting visible items from the */ /* top of the window downwards, */ /* starting with item number 0); */ /* */ /* Pointer to a hotlist_item struct */ /* representing the item clicked on; */ /* */ /* State of the mouse buttons, as */ /* Wimp_GetPointerInfo would return */ /* for clicks on a window of button */ /* type 10 (Double/Click/Drag); */ /* */ /* Screen x coordinate of the click; */ /* */ /* Screen y coordinate of the click. */ /*************************************************/ static _kernel_oserror * hotlist_process_click_on_item(unsigned int itemno, hotlist_item * item, int buttons, int x, int y) { _kernel_oserror * e = NULL; switch (buttons) { /* The window button type is Double/Click/Drag, so */ /* this represents a double click with Select. */ case Wimp_MouseButtonSelect: { /* Open or close directories, open URLs in a new window */ if (last_selected_item == itemno) { switch(item->type) { case hl_directory: e = hotlist_directory_open_close(item, itemno); break; case hl_url: e = hotlist_launch_url(item); break; } if (e) show_error_ret(e); /* Deselect the item that was double-clicked upon */ item->flags &= ~HOTLIST_G_IS_SELECTED; e = hotlist_redraw_items(itemno, itemno); } } break; /* Double-click with Adjust */ case Wimp_MouseButtonAdjust: { /* Open or close directories, but for URLs, open them in */ /* a new window and close the hotlist. */ if (last_selected_item == itemno) { switch(item->type) { case hl_directory: e = hotlist_directory_open_close(item, itemno); break; case hl_url: { e = hotlist_launch_url(item); if (!e) toolbox_hide_object(0, hotlist_windowid); } break; } if (e) show_error_ret(e); /* Deselect the item that was double-clicked upon */ item->flags &= ~HOTLIST_G_IS_SELECTED; e = hotlist_redraw_items(itemno, itemno); } } break; /* Drag with Select */ case 64: { e = hotlist_start_drag(); } break; /* Drag with Adjust */ case 16: { /* If the item clicked upon wasn't selected, select it */ if (!(item->flags & HOTLIST_G_IS_SELECTED)) { item->flags |= HOTLIST_G_IS_SELECTED; last_selected_item = itemno; } /* Start the drag and redraw the item clicked upon */ e = hotlist_start_drag(); if (!e) e = hotlist_redraw_items(itemno, itemno); } break; /* Click with Select */ case 1024: { if (!(item->flags & HOTLIST_G_IS_SELECTED)) /* (Do nothing when selected) */ { /* Clear the selected flags of everything else */ e = hotlist_clear_selection(); /* Select this item and if a directory, all items within it */ item->flags |= HOTLIST_G_IS_SELECTED | HOTLIST_G_REDRAW_NOW; if (item->type == hl_directory) hotlist_set_flags(item->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); /* Redraw to reflect the selection */ if (!e) e = hotlist_redraw_now(); } last_selected_item = itemno; } break; /* Click with Adjust */ case 256: { /* Swap the selected state of the item */ item->flags ^= HOTLIST_G_IS_SELECTED; item->flags |= HOTLIST_G_REDRAW_NOW; last_selected_item = itemno; /* If a directory, select or deselect the contents */ if (item->type == hl_directory) { if (item->flags & HOTLIST_G_IS_SELECTED) { hotlist_set_flags(item->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); } else { hotlist_clear_flags(item->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); } } /* Redraw to reflect the selection */ e = hotlist_redraw_now(); } break; } /* Return any errors that may have been generated */ return e; } /*************************************************/ /* hotlist_process_click() */ /* */ /* This function deals with mouse clicks on the */ /* hotlist window. */ /* */ /* Parameters: X position of click, in window */ /* coords; */ /* */ /* Y position of click, in window */ /* coords; */ /* */ /* Button state (as returned by */ /* Wimp_GetPointerInfo). */ /*************************************************/ static _kernel_oserror * hotlist_process_click(int x, int y, int buttons) { _kernel_oserror * e = NULL; hotlist_item * item; unsigned int item_height, item_dir_width, item_url_width; unsigned int itemno; int xmin, xmax; /* Get information on what was clicked upon */ RetError(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) RetError(hotlist_get_shape(&xmin, &xmax, item)); if (item && x >= xmin && x <= xmax) { /* If we have a specific item, process the click on it */ return hotlist_process_click_on_item(itemno, item, buttons, x, y); } else { /* Otherwise, actions depend on the button type */ switch(buttons) { /* Drag with Select */ case 64: { RetError(hotlist_clear_selection()); e = hotlist_selection_box_start(); } break; /* Drag with Adjust */ case 16: { e = hotlist_selection_box_start(); } break; /* Click with Select */ case 1024: { e = hotlist_clear_selection(); last_selected_item = 0xffffffff; } break; /* Click with Adjust */ case 256: { last_selected_item = 0xffffffff; } break; } } return e; } /*************************************************/ /* hotlist_preopen() */ /* */ /* This function should be called before opening */ /* the hotlist window. If the window is already */ /* showing, then the extent may be altered but */ /* it will never shrink the visible area of the */ /* window. If the window is currently closed, it */ /* will set the extent to the minimum possible */ /* value, which may well drag the visible area */ /* down too. */ /* */ /* Returns: 1 if window was already open, */ /* or 0 if it was closed. */ /*************************************************/ static int hotlist_preopen(void) { _kernel_oserror * e; WimpGetWindowStateBlock state; BBox bbox; ObjectId parent_id; ComponentId parent_component; unsigned int item_height, item_dir_width, item_url_width; unsigned int number, maxlen; unsigned int objectstate; int height, width; #define HotlistPreopen_ShowError(e) {if(e){show_error_ret(e);return 0;}} /* Is the window open or closed? */ e = toolbox_get_object_state(0, hotlist_windowid, &objectstate); HotlistPreopen_ShowError(e); /* Get entry sizes */ e = hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); HotlistPreopen_ShowError(e); /* General information */ number = hotlist_count_displayed_items(hotlist_root->data.directory_content); maxlen = hotlist_get_max_width(hotlist_root->data.directory_content) + 4; /* Find out window details */ e = window_get_wimp_handle(0, hotlist_windowid, &state.window_handle); HotlistPreopen_ShowError(e); e = wimp_get_window_state(&state); HotlistPreopen_ShowError(e); /* Sanity check... */ if (number < HOTLIST_WINDOW_MIN_HEIGHT) number = HOTLIST_WINDOW_MIN_HEIGHT; if (maxlen < HOTLIST_WINDOW_MIN_WIDTH) maxlen = HOTLIST_WINDOW_MIN_WIDTH; /* If the window is open, get its extent */ if (objectstate & Toolbox_GetObjectState_Showing) { e = window_get_extent(0, hotlist_windowid, &bbox); HotlistPreopen_ShowError(e); } /* Work out the y extent and x extent required for showing */ /* as much of the visible items in the hotlist as possible */ bbox.ymin = -number * item_height; bbox.xmax = maxlen; /* If the window was already open, don't let the visible */ /* area change - otherwise go for a best fit to the */ /* items, as worked out above. */ if (objectstate & Toolbox_GetObjectState_Showing) { 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; } /* Account for a button bar, if one were present. */ bbox.ymax = hotlist_bbar_size; bbox.xmin = 0; /* Set the extent */ e = window_set_extent(0, hotlist_windowid, &bbox); HotlistPreopen_ShowError(e); /* (Re)open the window */ if (objectstate & Toolbox_GetObjectState_Showing) { e = window_get_wimp_handle(0, hotlist_windowid, &state.window_handle); if (!e) e = wimp_get_window_state(&state); if (!e) e = toolbox_get_parent(0, hotlist_windowid, &parent_id, &parent_component); if (!e) e = toolbox_show_object(0, hotlist_windowid, Toolbox_ShowObject_FullSpec, &(state.visible_area), parent_id, parent_component); HotlistPreopen_ShowError(e); } return !!(objectstate & Toolbox_GetObjectState_Showing); } /*************************************************/ /* hotlist_redraw_handler() */ /* */ /* This handles redraw events from the Wimp, for */ /* the hotlist window. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ static int hotlist_redraw_handler(int event_code, WimpPollBlock * event, IdBlock * id_block, void * handle) { _kernel_oserror * e = NULL; WimpRedrawWindowBlock block; unsigned int item_height, item_dir_width, item_url_width; unsigned int first_item, last_item; int more; /* Get entry sizes */ ChkError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); block.window_handle = event->redraw_window_request.window_handle; /* Start the redraw loop */ wimp_redraw_window(&block, &more); while (more && !e) { /* Work out which bits to redraw... */ 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; /* ...and redraw them */ e = hotlist_draw(hotlist_root->data.directory_content, first_item, last_item); /* Get the next redraw rectangle */ if (!e) e = wimp_get_rectangle(&block, &more); } return 1; } /*************************************************/ /* hotlist_mouse_click_handler() */ /* */ /* Event handler to deal with mouse clicks in */ /* the hotlist window; converts coordinates from */ /* screen to window, and runs the result through */ /* hotlist_process_click. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ 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); ChkError(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 1; } /*************************************************/ /* hotlist_menuclose_handler() */ /* */ /* Called when menus close. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_menuclose_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId submenu_id; /* Deselect any items selected by the menu */ if (menu_select) { hotlist_clear_selection(); menu_select = 0; } /* Get rid of any submenus */ ChkError(menu_get_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, &submenu_id)); if (submenu_id) ChkError(toolbox_delete_object(0, submenu_id)); ChkError(menu_set_sub_menu_show(0, id_block->self_id, HOTLIST_URL_MENUITEM, 0)); return 1; } /*************************************************/ /* hotlist_set_menu_details() */ /* */ /* When a menu is to be opened or the hotlist */ /* state changes in a way that can affect open */ /* menus, this can be used to update the menu */ /* contents. */ /* */ /* Parameters: Object ID of the hotlist root */ /* menu. */ /*************************************************/ static _kernel_oserror * hotlist_set_menu_details(ObjectId menuid) { hotlist_item * item; char entrytext[Limits_Hotlist_ItemName]; ObjectId submenu_id; switch (hotlist_count_selected_items()) { /* No selected items - set the URL/Directory name string to */ /* "URL ''" and grey it out, along with the Clear Selection */ /* entry. */ case 0: { RetError(menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, "URL ''")); RetError(menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 1)); RetError(menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 1)); } break; /* Only 1 item selected */ case 1: { int name_len; item = hotlist_find_selected_item(); switch(item->type) { /* One directory selected - set the URL/Directory name */ /* string to a relevant value, and create and attach */ /* the relevant submenu. */ case hl_directory: { StrNCpy0(entrytext, "Dir. '"); RetError(toolbox_create_object(0, "HLDirmenu", &submenu_id)); RetError(menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id)); /* If the item is read-only, grey out the 'delete' entry */ if (item->flags & HOTLIST_G_IS_READ_ONLY) menu_set_fade(0, submenu_id, HOTLIST_DELETE_SUBMENUITEM, 1); else menu_set_fade(0, submenu_id, HOTLIST_DELETE_SUBMENUITEM, 0); } break; /* Similarly, deal with a single URL being selected */ case hl_url: { StrNCpy0(entrytext, "URL '"); RetError(toolbox_create_object(0, "HLURLmenu", &submenu_id)); RetError(menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id)); if (item->flags & HOTLIST_G_IS_READ_ONLY) menu_set_fade(0, submenu_id, HOTLIST_DELETE_SUBMENUITEM, 1); else menu_set_fade(0, submenu_id, HOTLIST_DELETE_SUBMENUITEM, 0); } break; } name_len = strlen(item->name); /* Add the item name in. If the whole item will not fit, put as much as */ /* will do, followed by '...'. */ if (name_len < sizeof(entrytext) - strlen(entrytext) - 1) strcat(entrytext, item->name); /* ('<' accounts for terminator, -1 accounts for closing "'") */ else if (sizeof(entrytext) - strlen(entrytext) > 5) { strncat(entrytext, item->name, sizeof(entrytext) - strlen(entrytext) - 5); /* (-5 accounts for closing "'", "..." and terminator) */ strcat(entrytext, "..."); } if (strlen(entrytext) < sizeof(entrytext) - 1) strcat(entrytext, "'"); /* Set the text, unfade the item, and unfade the Clear Selection item */ RetError(menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, entrytext)); RetError(menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 0)); RetError(menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 0)); } break; /* Many items selected */ default: { /* Set the URL/Directory name string, ungrey that item and Clear Selection, */ /* and create and attach an appropriate submenu. */ RetError(menu_set_entry_text(0, menuid, HOTLIST_URL_MENUITEM, "Selection")); RetError(menu_set_fade(0, menuid, HOTLIST_URL_MENUITEM, 0)); RetError(menu_set_fade(0, menuid, HOTLIST_CLEARSELECTION_MENUITEM, 0)); RetError(toolbox_create_object(0, "HLSlctmenu", &submenu_id)); RetError(menu_set_sub_menu_show(0, menuid, HOTLIST_URL_MENUITEM, submenu_id)); } break; } /* If *everything* is selected, want to grey out Select All; */ /* else ungrey it. */ if (hotlist_contents_selected(hotlist_root)) RetError(menu_set_fade(0, menuid, HOTLIST_SELECTALL_MENUITEM, 1)) else RetError(menu_set_fade(0, menuid, HOTLIST_SELECTALL_MENUITEM, 0)) /* Set the ticks on 'Show Descriptions' or 'Show URLs' in the */ /* Display submenu. */ RetError(menu_get_sub_menu_show(0, menuid, HOTLIST_DISPLAY_MENUITEM, &submenu_id)); if (hl_show_urls) { RetError(menu_set_tick(0, submenu_id, HOTLIST_MENU_SHOWDESCRIPTIONS, 0)); RetError(menu_set_tick(0, submenu_id, HOTLIST_MENU_SHOWURLS, 1)); } else { RetError(menu_set_tick(0, submenu_id, HOTLIST_MENU_SHOWDESCRIPTIONS, 1)); RetError(menu_set_tick(0, submenu_id, HOTLIST_MENU_SHOWURLS, 0)); } /* Finished */ return NULL; } /*************************************************/ /* hotlist_menuopen_handler() */ /* */ /* Handles events raised when menus are about to */ /* be shown. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ 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; int xmin, xmax, window_handle; ObjectId sub_menu; /* Get entry sizes. */ ChkError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); if (event_code == EHotlistToBeShown) { /* Work out which item the pointer was over. */ ChkError(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); state.window_handle = window_handle; ChkError(wimp_get_window_state(&state)); ChkError(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; } ChkError(menu_get_sub_menu_show(0, id_block->self_id, HOTLIST_DISPLAY_MENUITEM, &sub_menu)); /* If there are no selected items, select the one the menu */ /* was opened over. */ if (hotlist_count_selected_items() == 0) { item = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); if (item) ChkError(hotlist_get_shape(&xmin, &xmax, item)); if (item && pointerblock.x >= xmin && pointerblock.x <= xmax) { item->flags |= HOTLIST_G_IS_SELECTED | HOTLIST_G_REDRAW_NOW; if (item->type == hl_directory) hotlist_set_flags(item->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); menu_select = 1; ChkError(hotlist_redraw_now()); } } /* Update the menus */ ChkError(hotlist_set_menu_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); */ /* */ /* 0 - save all of hotlist, */ /* 1 - only save selection portions, */ /* but in both cases obey the next */ /* parameter; */ /* */ /* 0 - don't save read-only items, */ /* 1 - allow saving of read-only */ /* items. */ /* */ /* 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 type, int save_read_only) { int written; /* Follow the directory list */ while (list) { /* Don't save read-only entries unless told to */ if (save_read_only || !(list->flags & HOTLIST_G_IS_READ_ONLY)) { switch (list->type) { /* Write a link for URLs */ case hl_url: { if (type == 0 || (type == 1 && list->flags & HOTLIST_G_IS_SELECTED)) { HotlistWrite(fprintf(fileptr, "<li><a href=\"%s\">%s</a>\n", list->data.url, list->name)); } } break; /* Write a heading for directories */ case hl_directory: { if (type == 0 || !hotlist_no_contents_selected(list->data.directory_content)) { if (type == 0 || (type == 1 && list->flags & HOTLIST_G_IS_SELECTED)) { HotlistWrite(fprintf(fileptr, "<h4>%s</h4>\n", list->name)); if (list->flags & HOTLIST_D_IS_OPEN) { HotlistWrite(fprintf(fileptr, "<ul><!--open-->\n")); } else { HotlistWrite(fprintf(fileptr, "<ul>\n")); } } /* Recursive call for the directory contents First, */ /* write the entry header. */ /* Do the contents */ RetError(hotlist_save_entries(fileptr, list->data.directory_content, type, save_read_only)); if (type == 0 || (type == 1 && list->flags & HOTLIST_G_IS_SELECTED)) { HotlistWrite(fprintf(fileptr, "</ul>\n")); } } } break; } } /* Continue down the list */ list = list->next; } return NULL; /* Error condition exit */ hotlist_save_error: RetLastE; } /*************************************************/ /* hotlist_save_hotlist() */ /* */ /* This function saves the hotlist as an HTML */ /* file. */ /* */ /* Parameters: Pointer to the filename to save */ /* to (null terminated); */ /* */ /* Pointer to a buffer containing */ /* any extra information to put in */ /* the top of the file (intended for */ /* multiuser builds); */ /* */ /* 0 to save all of hotlist, 1 to */ /* save only the selected portions. */ /*************************************************/ _kernel_oserror * hotlist_save_hotlist(char * filename, char * extradata, int type) { _kernel_oserror * e; FILE * fileptr; static char * local_path = NULL; int written; if (!filename || !*filename) return NULL; /* Canonicalise the path */ RetError(utils_canonicalise_path(filename, &local_path)); /* Ensure it is present */ e = utils_build_tree(local_path); if (e) { free(local_path); return e; } /* Could take a while... */ _swix(Hourglass_On, 0); /* Open the file for writng */ fileptr = fopen(local_path, "wb"); /* Complain if it fails */ if (fileptr == NULL) { free(local_path); RetLastE; } /* Write the extra data */ if (extradata) HotlistWrite(fprintf(fileptr, "%s", extradata)); /* Write the file header */ HotlistWrite(fprintf(fileptr, "<html>\n" "<head>\n" CHARSET_SPECIFIER "<title>")); HotlistWrite(fprintf(fileptr, "%s", lookup_token("HotlistHTMLTitle:Hotlist",0,0))); HotlistWrite(fprintf(fileptr, "</title>\n" "</head>\n" "<body>\n" "<ul>\n")); /* Fill in the body */ e = hotlist_save_entries(fileptr, hotlist_root->data.directory_content, type, type == 1 ? 1 : 0); /* If saving part of the hotlist, allow saving of read-only components */ if (e) { _swix(Hourglass_Off, 0); fclose(fileptr); free(local_path); return e; } /* Write the footer and close the file */ HotlistWrite(fprintf(fileptr, "</ul>\n")); HotlistWrite(fprintf(fileptr, "</body>\n")); HotlistWrite(fprintf(fileptr, "</html>\n")); fclose(fileptr); _swix(Hourglass_Off, 0); /* Set the filetype to HTML (0xfaf) */ e = _swix(OS_File, _INR(0,2), 18, local_path, FileType_HTML); free(local_path); return e; /* Error condition exit */ hotlist_save_error: if (fileptr) { fclose(fileptr); _swix(Hourglass_Off, 0); } free(local_path); RetLastE; } /*************************************************/ /* hotlist_save() */ /* */ /* Veneer onto hotlist_save_hotlist - saves all */ /* of the hotlist, created to preserve API. */ /* */ /* Parameters: Pointer to the filename to save */ /* to (null terminated). */ /*************************************************/ _kernel_oserror * hotlist_save(char * filename) { return hotlist_save_hotlist(filename, NULL, 0); /* Save entire hotlist */ } /*************************************************/ /* 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; int target_type; /* Go through the file in chunks */ target_type = 0; /* At end of directory */ 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)) { erb = *_kernel_last_oserror(); e = &erb; 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) { erb = *_kernel_last_oserror(); 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) { erb = *_kernel_last_oserror(); e = &erb; 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) { e = make_no_memory_error(3); 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, "><"); /* Get ready to add this item, provided that the URL string has */ /* ':/' in - i.e. looks fully specified */ if (strstr(url, ":/")) { if (target_type == 0) { e = hotlist_new_url(target, HOTLIST_POSITION_END, str_ptr, url); target_type = 1; } else { e = hotlist_new_url(target, HOTLIST_POSITION_AFTER, str_ptr, url); } if (e) goto hotlist_load_directory_exit; /* (See near end of function) */ target = hotlist_newitem; } } /* 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 */ if (target_type == 0) { e = hotlist_new_directory(target, next_directory_name, HOTLIST_POSITION_END, &new_dir); target_type = 1; } else { e = hotlist_new_directory(target, next_directory_name, HOTLIST_POSITION_AFTER, &new_dir); } if (e) goto hotlist_load_directory_exit; target = hotlist_newitem; if (strstr(string_buffer, "<!--open-->") != NULL) { new_dir->flags |= HOTLIST_D_IS_OPEN; } 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_discard() */ /* */ /* Empties the hotlist. The hotlist window is */ /* not updated (expected usage is to close the */ /* window shotly before or after discarding the */ /* contents). */ /*************************************************/ void hotlist_discard(void) { while (hotlist_root->data.directory_content) { hotlist_delete_item(hotlist_root->data.directory_content); } } /*************************************************/ /* 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; static char * local_path = NULL; FILE * fileptr; if (!filename || !*filename) return NULL; local_path = malloc(strlen(filename) + 1); if (!local_path) return NULL; strcpy(local_path, filename); /* First delete all the existing hotlist items */ hotlist_discard(); /* Open the file */ fileptr = fopen(local_path, "r"); if (fileptr == NULL) { free(local_path); 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 */ if (!e) e = hotlist_modified(HL_MODIFIED_LOAD); free(local_path); 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) { ObjectId toolbar; ObjectId menu_id; BBox bbox; unsigned int item_height, item_dir_width, item_url_width; char root[] = "Root"; /* Set some initial flags */ hotlist_dragging.drag_type = HOTLIST_NOT_DRAGGING; hotlist_dragging.using_adjust = 0; /* 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; /* Read sprite sizes to see if the sprites actually exist */ RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* 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, EHotlistToBeShown, hotlist_menuopen_handler, NULL)); RetError(event_register_toolbox_handler(menu_id, EHotlistHidden, hotlist_menuclose_handler, NULL)); /* Main menu items */ RetError(event_register_toolbox_handler(-1, EHotlistSelectAll, hotlist_select_all_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistClearSelect, hotlist_clear_selection_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistOpenAll, hotlist_menu_openall_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistCloseAll, hotlist_menu_closeall_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistDelete, hotlist_menu_delete_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistSaveToServer, hotlist_save_to_server_handler, /* a.k.a. Save As Default */ NULL)); /* Submenu warning events */ RetError(event_register_toolbox_handler(-1, EHotlistShowEditURL, hotlist_show_editurl_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistShowRenameDirectory, hotlist_show_rendirectory_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistShowNewURL, hotlist_show_newurl_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistShowNewDirectory, hotlist_show_newdirectory_handler, NULL)); /* Hotlist related dialogue events */ RetError(event_register_toolbox_handler(-1, EHotlistClose, hotlist_close_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistNewEditURLOK, hotlist_newedit_url_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistNewEditURLCancel, hotlist_reset_url_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistNewRenameDirectoryOK, hotlist_newren_directory_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistNewRenameDirectoryCancel, hotlist_reset_directory_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistShowDescriptions, hotlist_show_descriptions_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistShowURLs, hotlist_show_urls_handler, NULL)); RetError(event_register_toolbox_handler(-1, EHotlistCancelDrag, hotlist_drag_stop_handler, NULL)); return NULL; } /*************************************************/ /* hotlist_open() */ /* */ /* Opens the hotlist window. If already open, */ /* all this does is bring it to the front - it */ /* doesn't move it. The display type is changed, */ /* if required, in any case. */ /* */ /* 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) { int open, old_show_urls; _kernel_oserror * e; old_show_urls = hl_show_urls; /* Sets show descriptions / show URLs */ hl_show_urls = show_urls; /* Set the size of the window etc. */ open = hotlist_preopen(); /* Show the hotlist. We have to do this twice; if we try to centre */ /* it just the once, then that call seems to read the visible area */ /* as it was *before* hotlist_preopen changed the extent. */ /* */ /* This Is Bad. In any case, for opening centred, we want to open */ /* full size, if not already open. */ if (open) { /* Open window once to let the wimp/toolbox see what shape it really is, */ /* but don't fiddle with the visible area as the window was already open. */ e = toolbox_show_object(0, hotlist_windowid, Toolbox_ShowObject_Default, 0, 0, -1); } else { BBox extent; WindowShowObjectBlock show; RetError(window_get_extent(0, hotlist_windowid, &extent)); show.visible_area = extent; show.behind = -3; e = toolbox_show_object(0, hotlist_windowid, Toolbox_ShowObject_FullSpec, &show, 0, -1); /* Open it once again in the place the window is really wanted */ e = toolbox_show_object(0, hotlist_windowid, show_type, type, 0, -1); } /* Redraw the window if the display type has changed */ if (!e && open && hl_show_urls != old_show_urls) { hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content)); } return e; } /*************************************************/ /* hotlist_close() */ /* */ /* Closes the hotlist window. */ /*************************************************/ _kernel_oserror * hotlist_close(void) { if (hotlist_windowid) return toolbox_hide_object(0, hotlist_windowid); else return NULL; } /*************************************************/ /* hotlist_close_handler() */ /* */ /* Closes the hotlist window. . */ /*************************************************/ static int hotlist_close_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ChkError(hotlist_close()); return 1; } /*************************************************/ /* hotlist_select_all_handler() */ /* */ /* This function handles the 'select all' menu */ /* item. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_select_all_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId main_menu; menu_select = 0; /* Select everything */ hotlist_set_flags(hotlist_root, hl_ALL, HOTLIST_G_IS_SELECTED); hotlist_root->flags &= ~HOTLIST_G_IS_SELECTED; hotlist_redraw_now(); /* Get the main menu ID */ ChkError(window_get_menu(0, hotlist_windowid, &main_menu)); /* Update the main menu contents to reflect the new state */ hotlist_set_menu_details(main_menu); return 1; } /*************************************************/ /* hotlist_clear_selection_handler() */ /* */ /* This function handles the clear selection */ /* menu item */ /*************************************************/ static int hotlist_clear_selection_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId main_menu; menu_select = 0; /* Get the main menu ID */ ChkError(window_get_menu(0, hotlist_windowid, &main_menu)); /* Clear the selection */ hotlist_clear_selection(); /* Update the main menu */ hotlist_set_menu_details(main_menu); return 1; } /*************************************************/ /* hotlist_menu_openall_handler() */ /* */ /* Deal with the Open All menu item. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ 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; /* Set all Open flags, clear all Redraw Now flags, and force a redraw of everything */ 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); ChkError(hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content))); hotlist_preopen(); } return 1; } /*************************************************/ /* hotlist_menu_closeall_handler() */ /* */ /* Deal with the Close All menu item. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_menu_closeall_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { unsigned int noitems; ObjectId main_menu; if (hotlist_root->data.directory_content) { menu_itemno = 0; /* Clear all Open and Redraw Now flags, and deselect all items */ 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); ChkError(hotlist_clear_selection()); ChkError(hotlist_redraw_items(0, noitems)); /* Get the main menu ID */ ChkError(window_get_menu(0, hotlist_windowid, &main_menu)); /* Update the menus to reflect there's nothing selected */ ChkError(hotlist_set_menu_details(main_menu)); hotlist_preopen(); } return 1; } /*************************************************/ /* hotlist_menu_delete_handler() */ /* */ /* Deal with the Delete menu item. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_menu_delete_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { hotlist_item * item; unsigned int noitems; ObjectId hotlist_window; int deleted = 0; if (!id_block->ancestor_id) hotlist_window = id_block->self_id; else hotlist_window = id_block->ancestor_id; /* If there are no selected items, can't do anything */ noitems = hotlist_count_displayed_items(hotlist_root->data.directory_content); if (!hotlist_count_selected_items()) return 0; /* Delete all selected items */ while ((item = hotlist_find_selected_item()) != NULL) { if (item->flags & HOTLIST_G_IS_READ_ONLY) { /* Just clear selection on read only items */ item->flags &= ~HOTLIST_G_IS_SELECTED; } else { /* Delete read/write items */ deleted ++; hotlist_delete_item(item); } } /* Redraw */ hotlist_redraw_items(0, noitems); /* We may have no more work to do if all of the selected items */ /* were read-only; otherwise, 'deleted' will be non-zero. */ if (deleted) { /* Update the window extent */ hotlist_preopen(); /* The hotlist has been modified... */ hotlist_modified(HL_MODIFIED_DELETE); } /* Finished */ return 1; } /*************************************************/ /* hotlist_save_to_server_handler() */ /* */ /* Deal with the 'Save to server' menu item */ /* (also known as 'Save as default'). */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_save_to_server_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { #ifdef SINGLE_USER show_error_ret(hotlist_save(lookup_choice("HotlistSave:Browse:User.Hotlist",0,0))); #else show_error_ret(multiuser_save_hotlist()); #endif return 1; } /*************************************************/ /* hotlist_show_newurl_handler() */ /* */ /* This function fills in the edit / create URL */ /* dialogue to be a New URL object. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_show_newurl_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId dboxid; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_show_newurl_handler: Called\n"); #endif ChkError(menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid)); alter_new = HOTLIST_MENUSECTION_NEW; /* Set the title, clear the description writable and URL writable, */ /* and set the default action button text. 'Cancel' stays as */ /* 'Cancel'. */ ChkError(window_set_title(0, dboxid, lookup_token("HotlistCreateURLTitle:Create new URL",0,0))); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWURL_NAME, "")); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWURL_URL, "")); ChkError(actionbutton_set_text (0, dboxid, HOTLIST_NEWURL_NEW, lookup_token("HotlistCreateURLAction:Create",0,0))); return 1; } /*************************************************/ /* hotlist_show_editurl_handler() */ /* */ /* This function fills in the edit / create URL */ /* dialogue to be an Edit URL object. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_show_editurl_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { hotlist_item * item; ObjectId dboxid; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_show_editurl_handler: Called\n"); #endif /* Must have a selected item... */ item = hotlist_find_selected_item(); if (!item) return 0; ChkError(menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid)); alter_new = HOTLIST_MENUSECTION_ALTER; /* Set the title, description writable, URL writable, and */ /* default action button text. 'Cancel' stays as 'Cancel'. */ ChkError(window_set_title(0, dboxid, lookup_token("HotlistEditURLTitle:Edit URL",0,0))); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWURL_NAME, item->name)); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWURL_URL, item->data.url)); ChkError(actionbutton_set_text (0, dboxid, HOTLIST_NEWURL_NEW, lookup_token("HotlistEditURLAction:Alter",0,0))); /* If the item's flags show it is read-only, it can't be altered */ if (item->flags & HOTLIST_G_IS_READ_ONLY) set_gadget_state(dboxid, HOTLIST_NEWURL_NEW, 1); else set_gadget_state(dboxid, HOTLIST_NEWURL_NEW, 0); return 1; } /*************************************************/ /* hotlist_show_newdirectory_handler() */ /* */ /* This function fills in the edit / create */ /* directory dialogue to be a New Directory */ /* object. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_show_newdirectory_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId dboxid; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_show_newdirectory_handler: Called\n"); #endif ChkError(menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid)); alter_new = HOTLIST_MENUSECTION_NEW; /* Set the title, clear the writable contents, and set */ /* the default action button text. 'Cancel' stays as */ /* 'Cancel'. */ ChkError(window_set_title(0, dboxid, lookup_token("HotlistCreateDirTitle:Create new directory",0,0))); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWDIRECTORY_NAME, "")); ChkError(actionbutton_set_text (0, dboxid, HOTLIST_NEWDIRECTORY_NEW, lookup_token("HotlistCreateDirAction:Create",0,0))); return 1; } /*************************************************/ /* hotlist_show_rendirectory_handler() */ /* */ /* This function fills in the edit / create */ /* directory dialogue to be a Rename Directory */ /* object. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_show_rendirectory_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { hotlist_item * item; ObjectId dboxid; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_show_rendirectory_handler: Called\n"); #endif /* Must have a selected item... */ item = hotlist_find_selected_item(); if (!item) return 0; ChkError(menu_get_sub_menu_show(0, id_block->self_id, id_block->self_component, &dboxid)); alter_new = HOTLIST_MENUSECTION_ALTER; /* Set the title, writable contents, and default action */ /* button text. 'Cancel' stays as 'Cancel'. */ ChkError(window_set_title(0, dboxid, lookup_token("HotlistRenameDirTitle:Rename directory",0,0))); ChkError(writablefield_set_value(0, dboxid, HOTLIST_NEWDIRECTORY_NAME, item->name)); ChkError(actionbutton_set_text (0, dboxid, HOTLIST_NEWDIRECTORY_NEW, lookup_token("HotlistRenameDirAction:Rename",0,0))); /* If the item's flags show it is read-only, it can't be renamed */ if (item->flags & HOTLIST_G_IS_READ_ONLY) set_gadget_state(dboxid, HOTLIST_NEWDIRECTORY_NEW, 1); else set_gadget_state(dboxid, HOTLIST_NEWDIRECTORY_NEW, 0); return 1; } /*************************************************/ /* hotlist_newedit_url_handler() */ /* */ /* This function either alters the name and URL */ /* fields of a selected URL, or creates a new */ /* URL item in the directory that the pointer */ /* was over when the main menu was opened. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_newedit_url_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { _kernel_oserror * e = NULL; hotlist_item * item; hotlist_item * tempitem; char * tempdesc; char * tempurl; int size; /* Is there a selected item? */ item = hotlist_find_selected_item(); /* Get the length of the description (name) text */ ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWURL_NAME, NULL, 0, &size)); /* Try to allocate space for it */ tempdesc = malloc(size + 1); /* '+1' to be safe (above call *should* return 'size of buffer required'...) */ if (!tempdesc) { show_error_cont(make_no_memory_error(15)); return 1; } /* Get the value, making absolutely sure it's terminated */ ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWURL_NAME, tempdesc, size, NULL)); tempdesc[size] = 0; /* Do the same for the URL */ ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWURL_URL, NULL, 0, &size)); tempurl = malloc(size + 1); if (!tempurl) { free(tempdesc); show_error_cont(make_no_memory_error(15)); return 1; } ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWURL_URL, tempurl, size, NULL)); tempurl[size] = 0; /* 'alter_new' is set by the menu handling functions to */ /* reflect whether we should create or alter items. */ switch (alter_new) { case HOTLIST_MENUSECTION_NEW: { /* Create a new item. First, find the one the menu was */ /* opened over. */ tempitem = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); /* If we can't, add to the start of the root directory */ if (!tempitem) { menu_itemno = 0; e = hotlist_new_url(hotlist_root, HOTLIST_POSITION_BEGINNING, tempdesc, tempurl); } /* Otherwise add it after the item, unless the parent is read-only */ else { if (tempitem->parent && (tempitem->parent->flags & HOTLIST_G_IS_READ_ONLY)) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("HLNewRO:You can't create an item here (the directory is read-only).", 0,0)); e = &erb; } else { e = hotlist_new_url(tempitem, HOTLIST_POSITION_AFTER, tempdesc, tempurl); } } if (!e) { /* Ensure the window extent is up to date */ hotlist_preopen(); /* Deal with redraw issues */ e = hotlist_redraw_items(menu_itemno, hotlist_count_displayed_items(hotlist_root->data.directory_content)); hotlist_clear_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); /* Call the 'hotlist has changed' function */ hotlist_modified(HL_MODIFIED_ADD); } /* Free temporary data and report any errors */ free(tempdesc); free(tempurl); ChkError(e); } break; case HOTLIST_MENUSECTION_ALTER: { /* Alter an existing item. If it is read-only, complain; */ /* otherwise, free its URL and description text. */ if (item->flags & HOTLIST_G_IS_READ_ONLY) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NLAlterRO:You can't alter this item (it is read-only).", 0,0)); e = &erb; free(tempdesc); free(tempurl); } else { free(item->name); free(item->data.url); /* Point to the new URL and description */ item->name = tempdesc; item->data.url = tempurl; /* Deal with redraw issues */ item->flags |= HOTLIST_G_REDRAW_NOW; hotlist_preopen(); e = hotlist_redraw_now(); /* Call the 'hotlist has changed' function */ hotlist_modified(HL_MODIFIED_ALTER); } /* Report any errors */ ChkError(e); } break; } return 1; } /*************************************************/ /* hotlist_newren_directory_handler() */ /* */ /* As hotlist_newedit_url_handler, but for */ /* directory items. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_newren_directory_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { _kernel_oserror * e = NULL; hotlist_item * item; hotlist_item * tempitem; char * tempname; int size; /* This bit is similar to the start of hotlist_newedit_url_handler */ item = hotlist_find_selected_item(); ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWDIRECTORY_NAME, NULL, 0, &size)); tempname = malloc(size + 1); if (!tempname) { show_error_cont(make_no_memory_error(16)); return 1; } ChkError(writablefield_get_value(0, id_block->self_id, HOTLIST_NEWDIRECTORY_NAME, tempname, size, NULL)); switch (alter_new) { case HOTLIST_MENUSECTION_NEW: { tempitem = hotlist_find_item(hotlist_root->data.directory_content, menu_itemno); /* If we can't find the item the pointer was near, at at the */ /* start of the root; else add near the item. */ if (!tempitem) { menu_itemno = 0; e = hotlist_new_directory(hotlist_root, tempname, HOTLIST_POSITION_END, &tempitem); } else { /* The parent may be read-only */ if (tempitem->parent && (tempitem->parent->flags & HOTLIST_G_IS_READ_ONLY)) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("HLNewRO:You can't create an item here (the directory is read-only).", 0,0)); e = &erb; } else { e = hotlist_new_directory(tempitem, tempname, HOTLIST_POSITION_AFTER, &tempitem); } } if (!e) { hotlist_preopen(); e = hotlist_redraw_items(menu_itemno, hotlist_count_displayed_items(hotlist_root->data.directory_content)); hotlist_clear_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); hotlist_modified(HL_MODIFIED_ADD); } /* Free the temporary block and report any errors */ free(tempname); ChkError(e); } break; case HOTLIST_MENUSECTION_ALTER: { /* Alter an existing directory. If it is read-only, complain. */ if (item->flags & HOTLIST_G_IS_READ_ONLY) { erb.errnum = Utils_Error_Custom_Message; StrNCpy0(erb.errmess, lookup_token("NLAlterRO:You can't alter this item (it is read-only).", 0,0)); e = &erb; free(tempname); } else { /* Free the item's existing name, link to the new */ /* name, and do the relevant redraws / hotlist */ /* modified calls. */ free(item->name); item->name = tempname; item->flags |= HOTLIST_G_REDRAW_NOW; hotlist_preopen(); e = hotlist_redraw_now(); hotlist_modified(HL_MODIFIED_ALTER); } /* Report any errors */ ChkError(e); } break; } return 1; } /*************************************************/ /* hotlist_reset_url_handler() */ /* */ /* This function is called when Cancel is */ /* clicked on in the New (aka Edit) URL�dialogue */ /* box. It resets the contents to their previous */ /* state. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_reset_url_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { /* Action depends on whether we were altering, or creating an item */ switch (alter_new) { case HOTLIST_MENUSECTION_NEW: { ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWURL_NAME, "")); ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWURL_URL, "")); } break; case HOTLIST_MENUSECTION_ALTER: { hotlist_item * item = hotlist_find_selected_item(); if (item) { ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWURL_NAME, item->name)); ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWURL_URL, item->data.url)); } } break; } return 1; } /*************************************************/ /* hotlist_reset_directory_handler() */ /* */ /* Similar to hotlist_reset_url_handler, but for */ /* resetting the New (aka Rename) Directory */ /* dialogue. */ /* */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_reset_directory_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { switch (alter_new) { case HOTLIST_MENUSECTION_NEW: { ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWDIRECTORY_NAME, "")); } break; case HOTLIST_MENUSECTION_ALTER: { hotlist_item * item = hotlist_find_selected_item(); if (item) ChkError(writablefield_set_value(0, id_block->self_id, HOTLIST_NEWDIRECTORY_NAME, item->name)); } break; } return 1; } /*************************************************/ /* hotlist_drag_renderer() */ /* */ /* Renders a single hotlist item at 0,0 for use */ /* with DragAnObject. */ /* */ /* Parameters: Pointer to the item to render; */ /* */ /* Height of a hotlist item in OS */ /* units; */ /* */ /* Width of a hotlist directory */ /* sprite in OS units; */ /* */ /* Width of a hotlist URL sprite in */ /* OS units. */ /*************************************************/ static void hotlist_drag_renderer(hotlist_item * item, unsigned int item_height, unsigned int item_dir_width, unsigned int item_url_width) { /* DON'T put Printf's in here... Doesn't work, at least, */ /* not with PipeFS output methods. */ WimpIconBlock hotlist_iconblock; int temp_width; int text_width; /* Create an icon block for the sprite component */ hotlist_iconblock.flags = HOTLIST_SPRITE_ICON_FLAGS; hotlist_iconblock.data.is.sprite_area = (void *) sprite_block; /* Set appropriate sprite and width of sprite */ switch(item->type) { case hl_url: { hotlist_iconblock.data.is.sprite = !(item->flags & HOTLIST_G_IS_READ_ONLY) ? URL_SPRITE : RESOURCES_URL_SPRITE; hotlist_iconblock.data.is.sprite_name_length = strlen(hotlist_iconblock.data.is.sprite); temp_width = item_url_width; } break; case hl_directory: { hotlist_iconblock.data.is.sprite = !(item->flags & HOTLIST_G_IS_READ_ONLY) ? CLOSED_DIRECTORY_SPRITE : CLOSED_RESOURCES_DIRECTORY_SPRITE; hotlist_iconblock.data.is.sprite_name_length = strlen(hotlist_iconblock.data.is.sprite); temp_width = item_dir_width; } break; default: { temp_width = 0; } break; } /* Set the bounding box */ hotlist_iconblock.bbox.xmin = 2; hotlist_iconblock.bbox.xmax = 2 + temp_width; hotlist_iconblock.bbox.ymax = 2 + item_height; hotlist_iconblock.bbox.ymin = 2; /* Plot the item */ if (wimp_plot_icon(&hotlist_iconblock)) return; /* Bail out on error! */ /* Create an icon block for the text component */ if (item->type == hl_url && hl_show_urls) utils_text_width(item->data.url, &text_width, 0); else utils_text_width(item->name, &text_width, 0); hotlist_iconblock.flags = HOTLIST_TEXT_ICON_FLAGS_DRAG; /* Set the bounding box */ hotlist_iconblock.bbox.xmin = temp_width + 4; hotlist_iconblock.bbox.xmax = temp_width + text_width + 16; hotlist_iconblock.bbox.ymax = item_height; hotlist_iconblock.bbox.ymin = 4; /* Set the text according to the item type */ if (item->type == hl_url && hl_show_urls) { hotlist_iconblock.data.it.buffer = item->data.url; hotlist_iconblock.data.it.buffer_size = strlen(item->data.url); hotlist_iconblock.data.it.validation = NULL; } else { hotlist_iconblock.data.it.buffer = item->name; hotlist_iconblock.data.it.buffer_size = strlen(item->name); hotlist_iconblock.data.it.validation = NULL; } /* Plot the icon */ wimp_plot_icon(&hotlist_iconblock); /* Finished */ return; } /*************************************************/ /* hotlist_start_drag() */ /* */ /* Start a new drag operation inside the hotlist */ /* window. */ /*************************************************/ static _kernel_oserror * hotlist_start_drag(void) { /* Do the hard work elsewhere... */ RetError(hotlist_start_drag_backend()); /* Reset autoscroll */ RetError(hotlist_autoscroll(0)); /* Register NULL handler */ register_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); /* Finished */ return NULL; } /*************************************************/ /* hotlist_start_drag_backend() */ /* */ /* This function is called by hotlist_start_drag */ /* to do most of the work for starting a drag */ /* operation from within the hotlist window. A */ /* drag box is created, bounding all selected */ /* items. */ /*************************************************/ static _kernel_oserror * hotlist_start_drag_backend(void) { WimpGetWindowStateBlock state; WimpGetPointerInfoBlock pointerblock; WimpDragBox box; hotlist_item * item; unsigned int item_height, item_dir_width, item_url_width; BBox bbox; int xorigin, yorigin; int screenwidth, screenheight; /* Read the pointer position and get the hotlist item sizes */ RetError(wimp_get_pointer_info(&pointerblock)); RetError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* Record whether ot not Adjust (and only Adjust) was used */ hotlist_dragging.using_adjust = (pointerblock.button_state == Wimp_MouseButtonAdjust); /* Get the shape of the selected items */ hotlist_get_selected_shape(&bbox); /* Now get information on the hotlist window itself, */ /* and the current screen shape. */ RetError(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); RetError(wimp_get_window_state(&state)); xorigin = state.xscroll - state.visible_area.xmin; yorigin = state.yscroll - state.visible_area.ymax; /* Screen size, pixels */ screenwidth = bbc_modevar(-1, BBC_XWindLimit); screenheight = bbc_modevar(-1, BBC_YWindLimit); /* Convert to OS units */ screenwidth = (screenwidth + 1) << (bbc_modevar(-1, BBC_XEigFactor)); screenheight = (screenheight + 1) << (bbc_modevar(-1, BBC_YEigFactor)); /* Read CMOS - solid drags, or a dashed outline? */ if (_kernel_osbyte(161, 28, 0) & (1 << (8 + 1))) { _kernel_swi_regs regs; int redraw_params[4]; /* Solid drags */ item = hotlist_find_selected_item(); /* A single item, or several? */ if ( (hotlist_count_selected_items() == 1 && item->type == hl_url) || ( hotlist_count_selected_items() == 1 && item->type == hl_directory && ( !(item->flags & HOTLIST_D_IS_OPEN) || hotlist_no_contents_selected(item->data.directory_content) || item->data.directory_content == NULL ) ) ) { /* A single item. Set up the box shape */ box.dragging_box.xmin = bbox.xmin - xorigin; box.dragging_box.ymin = bbox.ymin - yorigin; box.dragging_box.xmax = bbox.xmax - xorigin; box.dragging_box.ymax = bbox.ymax - yorigin; /* Store details of the redraw */ 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; /* Fill in the register block */ 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); /* Start the drag - use DragAnObject, as then we can show */ /* the item's text, as seen in the hotlist window, during */ /* the drag. */ RetError(_kernel_swi(DragAnObject_Start, ®s, ®s)); /* Finished; flag that we're solid-dragging an object and exit */ hotlist_dragging.drag_type = HOTLIST_SOLID_DRAG_OBJECT; return NULL; } else { int width, height; /* Several items. */ RetError(read_sprite_size(SELECTION_SPRITE, &width, &height)); /* Set the box shape, based around the mouse pointer coordinates */ box.dragging_box.xmin = pointerblock.x - (width / 2 + 10); /* '+ 10' = aesthetics */ 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); /* Fill in the register block */ regs.r[0] = (1<<0) | (1<<2) | (1<<6) | (1<<7); regs.r[1] = (int) sprite_block; regs.r[2] = (int) "package"; regs.r[3] = (int) &(box.dragging_box); /* Start the drag - use DragASprite as there's no text to */ /* show with the 'package' icon. */ RetError(_kernel_swi(DragASprite_Start, ®s, ®s)); /* Finished; flag that we're solid-dragging an object and exit */ hotlist_dragging.drag_type = HOTLIST_SOLID_DRAG_SPRITE; return NULL; } } /* CMOS said - use a dashed outline drag. */ box.drag_type = Wimp_DragBox_DragFixedDash; /* Set the box shape */ box.dragging_box.xmin = bbox.xmin - xorigin; box.dragging_box.xmax = bbox.xmax - xorigin; box.dragging_box.ymin = bbox.ymin - yorigin; box.dragging_box.ymax = bbox.ymax - yorigin; /* Constrain the drag to the screen size */ box.parent_box.xmin = box.dragging_box.xmin - pointerblock.x; box.parent_box.xmax = box.dragging_box.xmax - pointerblock.x + screenwidth; box.parent_box.ymin = box.dragging_box.ymin - pointerblock.y; box.parent_box.ymax = box.dragging_box.ymax - pointerblock.y + screenheight; /* Start the drag */ RetError(wimp_drag_box(&box)); /* Finished; flag that we're dragging an object with a dashed outline and exit */ hotlist_dragging.drag_type = HOTLIST_BOX_DRAG; return NULL; } /*************************************************/ /* hotlist_drag_stop_handler() */ /* */ /* Terminates any hotlist drag in progress. */ /*************************************************/ static int hotlist_drag_stop_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { _kernel_oserror * e; int dragging = hotlist_dragging.drag_type; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_drag_stop_handler: Called\n"); #endif /* If we're not dragging in the hotlist, something weird is */ /* going on! So definitely, ignore the event... */ if (hotlist_dragging.drag_type == HOTLIST_NOT_DRAGGING) return 0; /* Make sure we clear the dragging flag as soon as possible. */ /* If it gets left switched on, then drags in other parts of */ /* the browser could get misinterpreted by the hotlist code. */ dragging = hotlist_dragging.drag_type; hotlist_dragging.drag_type = HOTLIST_NOT_DRAGGING; if (hotlist_current_highlighted) { hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; ChkError(hotlist_redraw_items(highlighted_itemno, highlighted_itemno)); hotlist_current_highlighted = NULL; } /* Deal with each dragging type */ switch (dragging) { /* Complain if we don't understand it */ default: { hotlist_dragging.drag_type = HOTLIST_NOT_DRAGGING; #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Value %d of hotlist_dragging.drag_type not understood in hotlist_drag_stop_handler", hotlist_dragging.drag_type); show_error_ret(&erb); #endif return 0; } break; case HOTLIST_SOLID_DRAG_OBJECT: { /* Stop drag an object */ e = _swix(DragAnObject_Stop, 0); deregister_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); ChkError(e); } break; case HOTLIST_SOLID_DRAG_SPRITE: { /* Stop drag a sprite */ e = _swix(DragASprite_Stop, 0); deregister_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); ChkError(e); } break; case HOTLIST_BOX_DRAG_SELECTION: { /* Stop drag box */ e = wimp_drag_box(NULL); deregister_null_claimant(Wimp_ENull, hotlist_null_drag_select_handler, NULL); /* Deselect anything that is currently selected */ hotlist_clear_flags(hotlist_root->data.directory_content, hl_ALL, HOTLIST_G_DRAG_SELECTED); ChkError(hotlist_redraw_now()); ChkError(e); } break; case HOTLIST_BOX_DRAG: { e = wimp_drag_box(NULL); deregister_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); ChkError(e); } break; } return 1; } /*************************************************/ /* 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 in any */ /* other window will try to save the relevant */ /* datatype (URI or HTML) to that window. */ /*************************************************/ static int hotlist_drag_completed_handler(int event_code, WimpPollBlock * event, IdBlock * id_block, void * handle) { WimpGetWindowStateBlock state; WimpGetPointerInfoBlock pointerblock; hotlist_item * targetitem; hotlist_item * sourceitem; int window_handle; unsigned int item_height, item_dir_width, item_url_width; unsigned int top, bottom; int xmin, xmax; int winx, winy; int position; int dragging; int shift = 0; #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_drag_completed_handler: Called\n"); #endif /* If we're not dragging in the hotlist, the Wimp returned */ /* this because someone else completed a drag (e.g. a save */ /* dialogue). In that case, ignore the event. */ if (hotlist_dragging.drag_type == HOTLIST_NOT_DRAGGING) return 0; /* Make sure we clear the dragging flag as soon as possible. */ /* If it gets left switched on, then drags in other parts of */ /* the browser could get misinterpreted by the hotlist code. */ dragging = hotlist_dragging.drag_type; hotlist_dragging.drag_type = HOTLIST_NOT_DRAGGING; /* Check if SHIFT is pressed */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 128, &shift); /* Reset autoscrolling (To restore pointer if necessary) */ show_error_ret(hotlist_autoscroll(0)); /* Deal with each dragging type */ switch (dragging) { /* Complain if we don't understand it */ default: { #ifdef TRACE erb.errnum = Utils_Error_Custom_Normal; sprintf(erb.errmess, "Value %d of hotlist_dragging.drag_type not understood in hotlist_drag_completed_handler", hotlist_dragging.drag_type); show_error_ret(&erb); #endif return 0; } break; /* For e.g. dragging a single URL or directory item */ case HOTLIST_SOLID_DRAG_OBJECT: { _swix(DragAnObject_Stop, 0); } break; /* For e.g. dragging a selection under the 'package' icon */ case HOTLIST_SOLID_DRAG_SPRITE: { _swix(DragASprite_Stop, 0); } break; /* For e.g. dragging a single URL or selection without solid drags emables */ case HOTLIST_BOX_DRAG: { /* No special action required in this case */ } break; /* When dragging out a box to dynamically select hotlist items */ case HOTLIST_BOX_DRAG_SELECTION: { deregister_null_claimant(Wimp_ENull, hotlist_null_drag_select_handler, NULL); hotlist_convert_drag_selection(hotlist_root->data.directory_content); return 0; } break; } /* If there is a currently highlighted directory (i.e. something */ /* was to be dropped into it) unhighlight that item. */ if (hotlist_current_highlighted) { hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; hotlist_redraw_items(highlighted_itemno, highlighted_itemno); hotlist_current_highlighted = NULL; } /* Remove the drag null handler */ deregister_null_claimant(Wimp_ENull, hotlist_null_handler, NULL); /* Remember the first selected item in the hotlist */ sourceitem = hotlist_find_selected_item(); /* Get information on the pointer */ wimp_get_pointer_info(&pointerblock); /* Was the drag ended over the hotlist window? */ ChkError(window_get_wimp_handle(0, hotlist_windowid, &window_handle)); if (window_handle == pointerblock.window_handle) { /* Yes, the drag ended over the hotlist window. */ if (pointerblock.icon_handle != -1) return 0; /* (Only understand drops on workspace) */ /* Find item sizes, the window x and y coordinate of the */ /* drop point, and from this, the targetted hotlist item. */ ChkError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); state.window_handle = pointerblock.window_handle; ChkError(wimp_get_window_state(&state)); winx = pointerblock.x + (state.xscroll - state.visible_area.xmin); winy = pointerblock.y + (state.yscroll - state.visible_area.ymax); top = (-winy) / item_height; targetitem = hotlist_find_item(hotlist_root->data.directory_content, top); /* Decide where to put the items */ if (targetitem) { /* If we've dropped on a selected or read-only item, do nothing */ if (targetitem->flags & (HOTLIST_G_IS_SELECTED | HOTLIST_G_IS_READ_ONLY)) return 0; /* Otherwise, find the x coordinate span of the target item... */ hotlist_get_shape(&xmin, &xmax, targetitem); if (targetitem->type == hl_directory && winx >= xmin && winx <= xmin + item_dir_width) { /* ...and if the drop point was within the x span of the */ /* directory icon part of a directory, place in that */ /* directory (directories will only show the '+' icon, */ /* i.e. highlighted, when the pointer hovers over the */ /* sprite component). */ position = HOTLIST_POSITION_BEGINNING; } else { if ((-winy % item_height) > item_height / 2) { /* If we're over half way past the item, place after it, unless */ /* it is an open directory. Again, the null handler dealing */ /* with highlighting directory sprites reflects this situation */ /* correctly. */ if ( targetitem->type == hl_directory && targetitem->flags & HOTLIST_D_IS_OPEN ) { position = HOTLIST_POSITION_BEGINNING; } else { position = HOTLIST_POSITION_AFTER; } } else { /* Otherwise, place before */ position = HOTLIST_POSITION_BEFORE; } } } else { /* If there is no target item, add to the end of the hotlist root */ targetitem = hotlist_root; position = HOTLIST_POSITION_END; } /* Find the selected item. If its number is less than the one */ /* we estimated from the drop y coordinate, use that instead. */ { int selected; selected = hotlist_find_no_from_item(hotlist_find_selected_item()); if (selected < top) top = selected; } /* Count how many items are displayed to get the last hotlist item */ bottom = hotlist_count_displayed_items(hotlist_root->data.directory_content); if (!shift) { /* If shift isn't pressed, move selected items */ do { /* Ensure the source item is deselected */ sourceitem->flags &= ~HOTLIST_G_IS_SELECTED; /* Move it */ ChkError(hotlist_move_item(sourceitem, targetitem, position)); /* If this is a directory, deselect its contents */ if (sourceitem->type == hl_directory) { hotlist_clear_flags(sourceitem->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); } targetitem = sourceitem; sourceitem = hotlist_find_selected_item(); /* Loop round moving all following items for as long as */ /* anything is still selected - making sure we copy */ /* them all after what we just added. */ position = HOTLIST_POSITION_AFTER; } while (sourceitem); ChkError(hotlist_modified(HL_MODIFIED_MOVE)); } else { /* Shift is pressed, so copy the items. The code is very */ /* similar in structure to the move stuff above. */ do { sourceitem->flags &= ~HOTLIST_G_IS_SELECTED; ChkError(hotlist_copy_item(sourceitem, targetitem, position, &targetitem)); if (sourceitem->type == hl_directory) { hotlist_clear_flags(sourceitem->data.directory_content, hl_ALL, HOTLIST_G_IS_SELECTED); } sourceitem = hotlist_find_selected_item(); position = HOTLIST_POSITION_AFTER; } while (sourceitem); ChkError(hotlist_modified(HL_MODIFIED_COPY)); } /* The displayed item count may have changed, so take the greatest */ /* value of this as the bottom item to redraw between. */ { int new_bottom = hotlist_count_displayed_items(hotlist_root->data.directory_content); if (new_bottom > bottom) bottom = new_bottom; } /* Ensure the window extent etc. is up to date */ hotlist_preopen(); /* Redraw the relevant regions of the window */ ChkError(hotlist_redraw_items(top, bottom)); } else { /* No - the drag did not end over the hotlist window */ if ( sourceitem && hotlist_count_selected_items() == 1 && sourceitem->type == hl_url ) { /* There is a single selected item which is a URL. So, save */ /* it as a URI file. */ hotlist_initiate_uri_save(sourceitem); } else { /* Can't save URI file when saving more than one URL, */ /* so save an HTML file instead. */ hotlist_initiate_html_save(lookup_token("HotlistLeafname:Hotlist",0,0)); } } /* If we were using Adjust, close the window */ if (hotlist_dragging.using_adjust) { hotlist_dragging.using_adjust = 0; ChkError(toolbox_hide_object(0, hotlist_windowid)); } /* Finished */ return 1; } /*************************************************/ /* hotlist_autoscroll() */ /* */ /* Auto-scrolls a window. */ /* */ /* Parameters: Object ID of the window to scroll */ /* or 0 to reset autoscrolling. */ /*************************************************/ static _kernel_oserror * hotlist_autoscroll(int window) { WimpGetWindowStateBlock state; ObjectId over_window; ObjectId parent; ComponentId component; BBox extent; int scroll_changed; int x, y, position; int autoscroll_newtime; static unsigned int scrolling, autoscroll_oldtime; static unsigned int mouse_shape = Mouse_Shape_Normal; /* Reset autoscroll handling if requested */ if (!window) { #ifdef TRACE if (tl & (1<<25)) Printf("hotlist_autoscroll: Resetting\n"); #endif scrolling = 0; mouse_set_pointer_shape(Mouse_Shape_Normal); mouse_shape = Mouse_Shape_Normal; return _swix(OS_ReadMonotonicTime, _OUT(0), &autoscroll_oldtime); } /* What window is the pointer over? */ RetError(window_get_pointer_info(0, &x, &y, NULL, &over_window, NULL)); /* Get information on the window given to the function */ RetError(window_get_wimp_handle(0, window, &state.window_handle)); RetError(wimp_get_window_state(&state)); RetError(window_get_extent(0, window, &extent)); /* Find location of pointer relative to the given window */ if ( x < state.visible_area.xmin || x > state.visible_area.xmax || y < state.visible_area.ymin || y > state.visible_area.ymax ) { /* Outside the visible area */ position = 0; } else if ( x > state.visible_area.xmin + choices.auto_scroll_margin && x < state.visible_area.xmax - choices.auto_scroll_margin && y > state.visible_area.ymin + choices.auto_scroll_margin && y < state.visible_area.ymax - choices.auto_scroll_margin ) { /* Pointer is inside the non-scrolling region of the given window */ position = 1; } else { /* Pointer is inside the scrolling region of the given window */ position = 2; } /* Work out what scroll settings to give, flagging if there is */ /* a change in 'scroll_changed'. */ scroll_changed = 0; if (position == 0 || position == 2) { /* Check position relative to the top/bottom of the window */ if (y > state.visible_area.ymax - choices.auto_scroll_margin) { /* At or over the top of the window */ if (state.yscroll < extent.ymax) { scroll_changed = 1; state.yscroll += (y - (state.visible_area.ymax - choices.auto_scroll_margin)); } } else { if (y < state.visible_area.ymin + choices.auto_scroll_margin) { /* At or under the bottom of the window */ if (state.yscroll > extent.ymin + (state.visible_area.ymax - state.visible_area.ymin)) { scroll_changed = 1; state.yscroll -= ((state.visible_area.ymin + choices.auto_scroll_margin) - y); } } } /* Check the position relative to the left/right of the window, too */ if (x > state.visible_area.xmax - choices.auto_scroll_margin) { /* At or to the right of the right hand edge of the window */ if (state.xscroll < extent.xmax - (state.visible_area.xmax - state.visible_area.xmin)) { scroll_changed = 1; state.xscroll += (x - (state.visible_area.xmax - choices.auto_scroll_margin)); } } else { if (x < state.visible_area.xmin + choices.auto_scroll_margin) { /* At or to the left of the left hand edge of the window */ if (state.xscroll > extent.xmin) { scroll_changed = 1; state.xscroll -= ((state.visible_area.xmin + choices.auto_scroll_margin) - x); } } } } /* Deal with flags and the mouse pointer */ switch (position) { /* If we're outside the visible area of the window, and not scrolling, */ /* set the pointer back to a normal shape (else leave it alone). */ case 0: { if (!scrolling) { RetError(_swix(OS_ReadMonotonicTime, _OUT(0), &autoscroll_oldtime)); if (mouse_shape != Mouse_Shape_Normal) { mouse_set_pointer_shape(Mouse_Shape_Normal); mouse_shape = Mouse_Shape_Normal; } } } break; /* If we're in the non-scrolling region, unset the 'scrolling' flag */ /* and set the mouse pointer back to a normal shape. */ case 1: { scrolling = 0; RetError(_swix(OS_ReadMonotonicTime, _OUT(0), &autoscroll_oldtime)); if (mouse_shape != Mouse_Shape_Normal) { mouse_set_pointer_shape(Mouse_Shape_Normal); mouse_shape = Mouse_Shape_Normal; } } break; /* If inside the scrolling region, wait a while on the 'ToScroll' */ /* pointer, then start scrolling. */ case 2: { if (!scrolling && scroll_changed) { RetError(_swix(OS_ReadMonotonicTime, _OUT(0), &autoscroll_newtime)); /* If we exceed the autoscroll delay, set the scrolling flag */ if (autoscroll_newtime - autoscroll_oldtime > choices.auto_scroll_delay) { scrolling = 1; } /* Otherwise, set the 'ToScroll' pointer */ else { if (mouse_shape != Mouse_Shape_ToScroll) { mouse_shape = Mouse_Shape_ToScroll; mouse_set_pointer_shape(Mouse_Shape_ToScroll); } } } /* If the scroll position hasn't changed, update oldtime */ if (!scroll_changed) { RetError(_swix(OS_ReadMonotonicTime, _OUT(0), &autoscroll_oldtime)); } } break; } /* Closing actions to take if scrolling is in progress. */ if (scrolling) { if (scroll_changed) { /* If the scroll position has changed, update the window */ RetError(toolbox_get_parent(0, window, &parent, &component)); RetError(toolbox_show_object(0, window, Toolbox_ShowObject_FullSpec, &state.visible_area, parent, component)); /* Ensure the pointer shape is correct */ if (mouse_shape != Mouse_Shape_Scrolling) { mouse_shape = Mouse_Shape_Scrolling; mouse_set_pointer_shape(Mouse_Shape_Scrolling); } } else { /* If the scroll position has not changed, set the pointer */ /* shape to normal, and unset the scrolling flag - we've */ /* scrolled to the limit of the window extent. */ if (mouse_shape != Mouse_Shape_Normal) { mouse_shape = Mouse_Shape_Normal; mouse_set_pointer_shape(Mouse_Shape_Normal); scrolling = 0; } } } /* Finished */ return NULL; } /*************************************************/ /* hotlist_null_handler() */ /* */ /* Called every null event while a drag is in */ /* operation. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ static int hotlist_null_handler(int event_code, WimpPollBlock * event, IdBlock * id_block, void * handle) { WimpGetWindowStateBlock state; hotlist_item * item; ObjectId window; unsigned int item_height, item_dir_width, item_url_width; unsigned int itemno; int x, y, buttons; int xmin, xmax; int remove_highlight = 0; /* Get information on the pointer position */ ChkError(window_get_pointer_info(0, &x, &y, &buttons, &window, NULL)); /* Deal with autoscroll */ ChkError(hotlist_autoscroll(hotlist_windowid)); if (window == hotlist_windowid) { /* The pointer is over the hotlist window */ ChkError(window_get_wimp_handle(0, window, &state.window_handle)); /* Display arrow pointing into directory to say that dropping */ /* here will put in directory rather than next to it. First, */ /* get the window state. */ ChkError(wimp_get_window_state(&state)); /* Work out the item that the pointer is over */ ChkError(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); /* Is this a directory item which is already not selected? (The */ /* selected check is so that we don't end up highlighting the */ /* item we're dragging if this item is a directory). */ if (item && item->type == hl_directory && !(item->flags & HOTLIST_G_IS_SELECTED)) { ChkError(hotlist_get_shape(&xmin, &xmax, item)); /* If we're over the directory sprite, or half way under the item height when */ /* the directory is open. */ if ( ( x >= xmin && x <= xmin + item_dir_width ) || ( ((-y % item_height) > item_height / 2) && item->flags & HOTLIST_D_IS_OPEN ) ) { /* ...and the currently highlighted item isn't the one we're over, and */ /* it isn't read only... */ if ( hotlist_current_highlighted != item && !(item->flags & HOTLIST_G_IS_READ_ONLY) ) { /* ...then first, clear any highlighted item */ if (hotlist_current_highlighted) { hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; ChkError(hotlist_redraw_items(highlighted_itemno, highlighted_itemno)); } /* Now highlight the item we're over */ item->flags |= HOTLIST_D_IS_HIGHLIGHTED; hotlist_current_highlighted = item; highlighted_itemno = itemno; ChkError(hotlist_redraw_items(highlighted_itemno, highlighted_itemno)); /* Reset the auto-open timer */ ChkError(_swix(OS_ReadMonotonicTime, _OUT(0), &autoopen_oldtime)); } else if (hotlist_current_highlighted == item) { int new_time; /* If we've stayed over the same highlighted item, */ /* keep a count of for how long, and if required, */ /* automatically open the directory. */ ChkError(_swix(OS_ReadMonotonicTime, _OUT(0), &new_time)); if ( choices.auto_open_delay && !(item->flags & HOTLIST_D_IS_OPEN) && new_time - autoopen_oldtime > choices.auto_open_delay ) { /* Auto-open the directory */ item->flags |= HOTLIST_D_IS_OPEN; hotlist_preopen(); ChkError(hotlist_redraw_items(highlighted_itemno, hotlist_count_displayed_items(hotlist_root->data.directory_content))); } } } else { /* If we're not over a directory sprite, clear any existing */ /* highlight. */ remove_highlight = 1; } } /* If we're not over a directory at all, never mind it's directory */ /* sprite component, again, clear any existing highlight. */ else remove_highlight = 1; } /* The following executes if the pointer is not over the hotlist window */ else { /* Again, clear any highlighted directory sprite. */ remove_highlight = 1; } /* If we're not over a directory sprite, clear any existing */ /* highlight. */ if (remove_highlight && hotlist_current_highlighted) { hotlist_current_highlighted->flags &= ~HOTLIST_D_IS_HIGHLIGHTED; ChkError(hotlist_redraw_items(highlighted_itemno, highlighted_itemno)); hotlist_current_highlighted = NULL; } /* Finished */ return 0; } // // Simple non-scrolling selection box, to be improved later... // Workarea relative corner of selection box may not be needed. // static int selection_x, selection_y; /*************************************************/ /* hotlist_start_drag() */ /* */ /* This function is called to start a selection */ /* box operation in the hotlist window. */ /*************************************************/ static _kernel_oserror * hotlist_selection_box_start(void) { WimpGetPointerInfoBlock pointerblock; WimpGetWindowStateBlock state; WimpDragBox box; /* Get the pointer position and info on the hotlist window */ RetError(wimp_get_pointer_info(&pointerblock)); RetError(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); RetError(wimp_get_window_state(&state)); /* Remember the pointer position in window coordinates */ selection_x = pointerblock.x + (state.xscroll - state.visible_area.xmin); selection_y = pointerblock.y + (state.yscroll - state.visible_area.ymax); /* Set up the drag box structure */ box.drag_type = Wimp_DragBox_DragRubberDash; box.dragging_box.xmin = pointerblock.x; 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; box.parent_box.xmax = state.visible_area.xmax; box.parent_box.ymin = state.visible_area.ymin; box.parent_box.ymax = state.visible_area.ymax; /* Start the drag */ RetError(wimp_drag_box(&box)); /* Set global variable saying we are currently dragging */ /* so we know whether to process a user_drag_box event */ hotlist_dragging.drag_type = HOTLIST_BOX_DRAG_SELECTION; /* Install the null handler */ register_null_claimant(Wimp_ENull, hotlist_null_drag_select_handler, NULL); return NULL; } /*************************************************/ /* hotlist_null_drag_select_handler() */ /* */ /* This function is called as a null handler; */ /* it is responsible for selecting and */ /* deselecting items within and outside the */ /* rubber drag box started by */ /* hotlist_selection_box_start. */ /* */ /* Parameters are as standard for a Wimp event */ /* handler. */ /*************************************************/ static int hotlist_null_drag_select_handler(int event_code, WimpPollBlock * event, IdBlock * id_block, void * handle) { WimpGetPointerInfoBlock pointerblock; WimpGetWindowStateBlock state; unsigned int item_height, item_dir_width, item_url_width; unsigned int item_min, item_max; int workx, worky; int minx, maxx; /* Find the pointer position and get information on the hotlist window */ ChkError(wimp_get_pointer_info(&pointerblock)); ChkError(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); ChkError(wimp_get_window_state(&state)); ChkError(hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width)); /* Get the mouse position in window coordinates */ workx = pointerblock.x + (state.xscroll - state.visible_area.xmin); worky = pointerblock.y + (state.yscroll - state.visible_area.ymax); /* Work out the items spanned by the drag box */ 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; } /* Select them */ ChkError(hotlist_select_box(item_min, item_max, minx, maxx)); return 0; } /*************************************************/ /* hotlist_select_box() */ /* */ /* Sets the HOTLIST_G_DRAG_SELECTED flag for all */ /* visible items in the range first_item to */ /* last_item where their horizontal visible area */ /* intersects with the a line draw from minx to */ /* maxx. Clears the HOTLIST_G_DRAG_SELECTED */ /* flag for all other items. */ /* */ /* Parameters: Number of the first visible item; */ /* */ /* Number of the last visible item; */ /* */ /* minx (window coords); */ /* */ /* maxx (window coords). */ /*************************************************/ _kernel_oserror * hotlist_select_box(unsigned int first_item, unsigned int last_item, int minx, int maxx) { unsigned int itemno = 0; return hotlist_select_box_r(first_item, last_item, hotlist_root->data.directory_content, &itemno, minx, maxx); } /*************************************************/ /* hotlist_select_box_r() */ /* */ /* Recursive backend to hotlist_select_box. */ /* */ /* Parameters: Number of the first visible item; */ /* */ /* Number of the last visible item; */ /* */ /* First item in the directory to */ /* scan; */ /* */ /* Pointer to an int, in which the */ /* current item number is kept (for */ /* internal use only); */ /* */ /* minx (window coords); */ /* */ /* maxx (window coords). */ /*************************************************/ _kernel_oserror * hotlist_select_box_r(unsigned int first_item, unsigned int last_item, hotlist_item * item, unsigned int * itemno, int minx, int maxx) { _kernel_oserror * e; int itemxmin, itemxmax; while (item) { if (*itemno >= first_item && *itemno <= last_item) { /* As long as we're within the item number specified, deal with items */ RetError(hotlist_get_shape(&itemxmin, &itemxmax, item)); /* Select items within the horizontal range */ if (!(maxx < itemxmin || minx > itemxmax)) { if (!(item->flags & HOTLIST_G_DRAG_SELECTED)) { item->flags |= HOTLIST_G_DRAG_SELECTED; e = hotlist_redraw_items(*itemno, *itemno); /* If directory is closed, select everything in it (if it is open, */ /* we should only select items the drag box covers). */ if (item->type == hl_directory && !(item->flags & HOTLIST_D_IS_OPEN)) { hotlist_set_flags (item->data.directory_content, hl_ALL, HOTLIST_G_DRAG_SELECTED); hotlist_clear_flags(item->data.directory_content, hl_ALL, HOTLIST_G_REDRAW_NOW); } if (e) return e; } } /* Deselect items outside of the horizontal range */ else { if (item->flags & HOTLIST_G_DRAG_SELECTED) { item->flags &= ~HOTLIST_G_DRAG_SELECTED; e = hotlist_redraw_items(*itemno, *itemno); /* If directory is closed unselect everything in it */ if (item->type == hl_directory && !(item->flags & HOTLIST_D_IS_OPEN)) { hotlist_clear_flags(item->data.directory_content, hl_ALL, HOTLIST_G_DRAG_SELECTED | HOTLIST_G_REDRAW_NOW); } if (e) return e; } } } else { /* If we're not within the item numbers specified, then deselect things */ if (item->flags & HOTLIST_G_DRAG_SELECTED) { item->flags &= ~HOTLIST_G_DRAG_SELECTED; e = hotlist_redraw_items(*itemno, *itemno); /* If directory is closed unselect everything in it */ if (item->type == hl_directory && !(item->flags & HOTLIST_D_IS_OPEN)) { hotlist_clear_flags(item->data.directory_content, hl_ALL, HOTLIST_G_DRAG_SELECTED | HOTLIST_G_REDRAW_NOW); } if (e) return e; } } /* Increment the item number character */ *itemno += 1; /* Recurse for any open directory items */ if (item->type == hl_directory && item->flags & HOTLIST_D_IS_OPEN) { RetError(hotlist_select_box_r(first_item, last_item, item->data.directory_content, itemno, minx, maxx)); } /* Get the next item */ item = item->next; } /* Finished */ return NULL; } /*************************************************/ /* hotlist_contents_selected() */ /* */ /* Checks if all items and subdirectories are */ /* selected. */ /* */ /* Parameters: Pointer to the first hotlist_item */ /* to check. */ /* */ /* Returns: 1 if all items are selected, else */ /* 0 (there are unselected items). */ /*************************************************/ static unsigned int hotlist_contents_selected(hotlist_item * item) { while (item) { /* Keep trying to find an unselected item and */ /* immediately return 0 if found. */ if (!item->flags & HOTLIST_G_IS_SELECTED) return 0; if (item->type == hl_directory) { if (!hotlist_contents_selected(item->data.directory_content)) return 0; } item = item->next; } return 1; } /*************************************************/ /* hotlist_no_contents_selected() */ /* */ /* Checks if all items and subdirectories are */ /* unselected. */ /* */ /* Parameters: Pointer to the first hotlist_item */ /* to check. */ /* */ /* Returns: 1 if all items are unselected, */ /* or 0 (there are selected items). */ /*************************************************/ static unsigned int hotlist_no_contents_selected(hotlist_item * item) { while (item) { /* Keep trying to find a selected item and */ /* immediately return 0 if found. */ if (item->flags & HOTLIST_G_IS_SELECTED) return 0; if (item->type == hl_directory) { if (!hotlist_no_contents_selected(item->data.directory_content)) return 0; } item = item->next; } return 1; } /*************************************************/ /* hotlist_show_descriptions_handler() */ /* */ /* Selects Show Descriptions and redraws the */ /* hotlist window and relevant menus to reflect */ /* this. Parameters are as standard for a */ /* Toolbox event handler. */ /*************************************************/ static int hotlist_show_descriptions_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId main_menu; hl_show_urls = 0; /* Update the menus */ ChkError(window_get_menu(0, hotlist_windowid, &main_menu)); hotlist_set_menu_details(main_menu); /* Ensure the window is up to date */ hotlist_preopen(); /* Redraw as required */ ChkError(hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content))); return 1; } /*************************************************/ /* hotlist_show_urls_handler() */ /* */ /* Selects Show URLs and redraws the hotlist */ /* window and relevant menus to reflect this. */ /* Parameters are as standard for a Toolbox */ /* event handler. */ /*************************************************/ static int hotlist_show_urls_handler(int event_code, ToolboxEvent * event, IdBlock * id_block, void * handle) { ObjectId main_menu; hl_show_urls = 1; /* Update the menus */ ChkError(window_get_menu(0, hotlist_windowid, &main_menu)); hotlist_set_menu_details(main_menu); /* Ensure the window is up to date */ hotlist_preopen(); /* Redraw as required */ ChkError(hotlist_redraw_items(0, hotlist_count_displayed_items(hotlist_root->data.directory_content))); return 1; } /*************************************************/ /* hotlist_convert_drag_selection() */ /* */ /* Recurses through the hotlist_item structure */ /* altering all 'HOTLIST_G_DRAG_SELECTED' items */ /* to have 'HOTLIST_G_IS_SELECTED' set or unset */ /* as is appropriate. */ /* */ /* dragselected and selected = unselected */ /* dragselected = selected */ /* selected = selected */ /* neither = unselected */ /* */ /* Parameters: Pointer to a hotlist_item to */ /* start on. */ /*************************************************/ static void hotlist_convert_drag_selection(hotlist_item *item) { while (item) { if (item->flags & HOTLIST_G_DRAG_SELECTED) { item->flags &= ~HOTLIST_G_DRAG_SELECTED; if (item->flags & HOTLIST_G_IS_SELECTED) item->flags &= ~HOTLIST_G_IS_SELECTED; else item->flags |= HOTLIST_G_IS_SELECTED; } /* Recursive call for directories */ if (item->type == hl_directory) { hotlist_convert_drag_selection(item->data.directory_content); } item = item->next; } } /*************************************************/ /* hotlist_modified() */ /* */ /* Called whenever the hotlist is modified. */ /* */ /* Parameters: The operaton that modified the */ /* hotlist, as a number; see the */ /* HL_MODIFIED_... definitions in */ /* Hotlist.h. */ /*************************************************/ static _kernel_oserror * hotlist_modified(unsigned int type) { #ifdef TRACE if (tl & (1u<<25)) Printf("hotlist_modified: Called\n"); #endif if (choices.save_hotlist == Choices_SaveHotlist_Always) { /* If we've loaded a resources file, there's no point */ /* saving the hotlist because the read-only parts will */ /* not get saved anyway. */ if (type != HL_MODIFIED_LOADED_RESOURCES) { #ifdef SINGLE_USER return hotlist_save(lookup_choice("HotlistSave:Browse:User.Hotlist",0,0)); #else multiuser_save_hotlist(); return NULL; #endif } } return NULL; } /*************************************************/ /* hotlist_initiate_uri_save() */ /* */ /* Starts file transfer of a URI file to another */ /* application. */ /* */ /* Parameters: Pointer to the hotlist item to */ /* save as a URI file. */ /*************************************************/ static _kernel_oserror * hotlist_initiate_uri_save(hotlist_item * item) { WimpGetPointerInfoBlock block; WimpMessage message; int ctrl; /* Is Control being pressed? */ _swix(OS_Byte, _INR(0,1) | _OUT(1), 121, 129, &ctrl); /* Find out where the pointer is */ RetError(wimp_get_pointer_info(&block)); /* Get the leafname. Use a message block for the buffer because */ /* there's no point generating anything longer than can actually */ /* be sent out in a message. */ urlutils_leafname_from_url(item->data.url, message.data.data_save.leaf_name, sizeof(message.data.data_save.leaf_name)); /* Send Message_DataSave to start file transfer - a URI file, */ /* or if Control is held down, a URL file. */ return protocols_atats_send_data_save(NULL, item, message.data.data_save.leaf_name, save_uri_size(item->data.url, item->name, 0), ctrl ? FileType_URL : FileType_URI, protocols_saving_hotlist_entry, &block); } /*************************************************/ /* hotlist_initiate_html_save() */ /* */ /* Starts file transfer of a selection in the */ /* hotlist as an HTML file. */ /* */ /* Parameters: Pointer to a null-terminated */ /* leafname to save as. */ /*************************************************/ static _kernel_oserror * hotlist_initiate_html_save(char * filename) { WimpGetPointerInfoBlock block; WimpMessage message; RetError(wimp_get_pointer_info(&block)); /* Ensure the leafname isn't too long for a message */ StrNCpy0(message.data.data_save.leaf_name, filename); /* Send Message_DataSave to start file transfer */ return protocols_atats_send_data_save(NULL, NULL, message.data.data_save.leaf_name, -1, FileType_HTML, protocols_saving_hotlist_selection, &block); } /*************************************************/ /* hotlist_return_window_id() */ /* */ /* Returns: The toolbox object ID of the */ /* hotlist window. */ /*************************************************/ ObjectId hotlist_return_window_id(void) { return hotlist_windowid; } /*************************************************/ /* hotlist_add_position() */ /* */ /* Add a new URL to the hotlist at a specified */ /* position.� */ /* */ /* Parameters: Screen relative x position; */ /* */ /* Screen relative y position; */ /* */ /* Description of the URL (e.g. from */ /* the page title); */ /* */ /* Pointer to the URL itself. */ /*************************************************/ _kernel_oserror * hotlist_add_position(int x, int y, char * description, char * url) { WimpGetWindowStateBlock state; hotlist_item * targetitem; unsigned int item_height, item_dir_width, item_url_width; int winx, winy; int top, position; /* Calculate window relative coordinates */ RetError(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); RetError(wimp_get_window_state(&state)); winx = coords_x_toworkarea(x, (WimpRedrawWindowBlock *) &state); winy = coords_y_toworkarea(y, (WimpRedrawWindowBlock *) &state); /* Calculate which item add is over */ hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); top = (-winy) / item_height; targetitem = hotlist_find_item(hotlist_root->data.directory_content, top); /* Decide where to put the new item */ if (targetitem) { if ((-winy % item_height) > item_height / 2) { /* Put item after the target if we're over */ /* half way past its height */ position = HOTLIST_POSITION_AFTER; } else { /* Otherwise, put item before the target */ position = HOTLIST_POSITION_BEFORE; } } else { /* Put item at end of root directory */ position = HOTLIST_POSITION_END; targetitem = hotlist_root; } /* Create new item in appropriate place */ RetError(hotlist_new_url(targetitem, position, description, url)); hotlist_preopen(); /* Optimise the redraw to do as little as possible depending */ /* upon where in the list the item was added. */ switch (position) { case HOTLIST_POSITION_END: { RetError(hotlist_redraw_now()); } break; default: { hotlist_clear_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); RetError(hotlist_redraw_items(top - 1, hotlist_count_displayed_items(hotlist_root->data.directory_content))); } break; } /* Exit by calling the routine that we must call every */ /* time the hotlist changes (so that it can, for */ /* example, be saved - should the Choices require it. */ return hotlist_modified(HL_MODIFIED_ADD); } /*************************************************/ /* hotlist_add_html_file() */ /* */ /* Read in an HTML file, adding items starting */ /* at the specified position. */ /* */ /* Parameters: Screen relative x position; */ /* */ /* Screen relative y position; */ /* */ /* Pathname of the HTML file. */ /*************************************************/ _kernel_oserror * hotlist_add_html_file(int x, int y, char * path) { _kernel_oserror * e; hotlist_item * targetitem; hotlist_item * newitem; FILE * fileptr; WimpGetWindowStateBlock state; unsigned int item_height, item_dir_width, item_url_width; int winx, winy; int top, position; char * dirname; /* Calculate window relative coordinates */ RetError(window_get_wimp_handle(0, hotlist_windowid, &state.window_handle)); RetError(wimp_get_window_state(&state)); winx = coords_x_toworkarea(x, (WimpRedrawWindowBlock *) &state); winy = coords_y_toworkarea(y, (WimpRedrawWindowBlock *) &state); /* Calculate which item add is over */ hotlist_get_entry_sizes(&item_height, &item_dir_width, &item_url_width); top = (-winy) / item_height; targetitem = hotlist_find_item(hotlist_root->data.directory_content, top); /* Decide where to put the new item */ if (targetitem) { if ((-winy % item_height) > item_height / 2) { /* Put item after the target if we're over */ /* half way past its height */ position = HOTLIST_POSITION_AFTER; } else { /* Otherwise, put item before the target */ position = HOTLIST_POSITION_BEFORE; } } else { /* Put item at end of root directory */ position = HOTLIST_POSITION_END; targetitem = hotlist_root; } dirname = strrchr(path, '.'); if (dirname) dirname ++; if (!dirname || !*dirname) dirname = lookup_token("HotlistUntitled:(Untitled)",0,0); RetError(hotlist_new_directory(targetitem, dirname, position, &newitem)); /* Open the file */ fileptr = fopen(path, "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, adding to the new directory */ e = hotlist_load_directory(fileptr, newitem); fclose(fileptr); if (e) return e; /* Handle redraw */ hotlist_preopen(); hotlist_set_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); RetError(hotlist_redraw_now()); /* Exit through the 'has modified' routine */ return hotlist_modified(HL_MODIFIED_ADD); } /*************************************************/ /* hotlist_load_resources() */ /* */ /* Read in a read-only 'Resources' hotlist, at */ /* the top of the hotlist window. This will not */ /* be saved when the rest of the hotlist is. */ /* */ /* Errors are raised if there is not enough */ /* memory etc., but not if the file doesn't */ /* exist. */ /* */ /* Parameters: Pathname of the HTML file. */ /*************************************************/ _kernel_oserror * hotlist_load_resources(char * path) { _kernel_oserror * e; hotlist_item * newitem; FILE * fileptr; RetError(hotlist_new_directory(hotlist_root, lookup_token("HotlistResDir:Resources",0,0), HOTLIST_POSITION_BEGINNING, &newitem)); /* Open the file */ fileptr = fopen(path, "r"); /* Fail silently if there's an error opening it */ if (fileptr == NULL) return NULL; /* Load it, adding to the new directory */ e = hotlist_load_directory(fileptr, newitem); fclose(fileptr); if (e) return e; /* Set all contained items as read-only, and mark the top-level */ /* Resources directory as both read-only and, if the Choices */ /* say so, open. */ newitem->flags |= (HOTLIST_G_IS_READ_ONLY); hotlist_set_flags(newitem->data.directory_content, hl_ALL, HOTLIST_G_IS_READ_ONLY); if (!strcmp(lookup_choice("OpenResources:no",0,0), "yes")) { newitem->flags |= (HOTLIST_D_IS_OPEN); } /* Handle redraw */ hotlist_preopen(); hotlist_set_flags(hotlist_root, hl_ALL, HOTLIST_G_REDRAW_NOW); RetError(hotlist_redraw_now()); /* Exit through the 'has modified' routine */ return hotlist_modified(HL_MODIFIED_LOADED_RESOURCES); } /*************************************************/ /* hotlist_get_selected_shape() */ /* */ /* This routine gets the coordinates required to */ /* bound every selected item in the hotlist */ /* window (window relative coordinated) */ /* */ /* Parameters: Pointer to window relative */ /* bounding box. */ /* */ /* Returns: 1 if there were selected item(s), */ /* 0 if there weren't. */ /*************************************************/ static int hotlist_get_selected_shape(BBox * box) { _kernel_oserror * e; unsigned int itemno, item_height; int found; found = 0; itemno = 0; /* Get item sizes */ e = hotlist_get_entry_sizes(&item_height, NULL, NULL); if (e) { show_error_ret(e); return 0; } /* Enter the recursive routine */ e = hotlist_get_selected_shape_r(hotlist_root->data.directory_content, box, &itemno, &found, item_height); if (e) { show_error_ret(e); return 0; } return found; } /*************************************************/ /* hotlist_get_selected_shape_r() */ /* */ /* Recursive back-end to */ /* hotlist_get_selected_shape(). */ /* */ /* Parameters: Pointer to a hotlist_item; */ /* */ /* Pointer to window relative */ /* bounding box; */ /* */ /* Pointer to the current item */ /* number (for internal use); */ /* */ /* Pointer to found flag; */ /* */ /* The height of a single entry in */ /* OS units. */ /*************************************************/ static _kernel_oserror * hotlist_get_selected_shape_r(hotlist_item * list, BBox * box, unsigned int * itemno, int * found, unsigned int item_height) { int newxmin, newxmax; while (list) { if (list->flags & HOTLIST_G_IS_SELECTED) { RetError(hotlist_get_shape(&newxmin, &newxmax, list)); /* If we've already found an open item, then only extend the */ /* bounding box in appropriate directions. */ if (*found) { if (newxmin < box->xmin) box->xmin = newxmin; if (newxmax > box->xmax) box->xmax = newxmax; box->ymin = -((*itemno) + 1) * item_height; } else { /* This section run for first selected item found only; */ /* set the selected shape box to match the item's */ /* bounding box. */ box->xmin = newxmin; box->xmax = newxmax; box->ymax = -(*itemno) * item_height; box->ymin = -((*itemno) + 1) * item_height; /* Flag that we've found a selected item */ *found = 1; } } *itemno += 1; /* Recursively scan all open directories */ if (list->type == hl_directory && (list->flags & HOTLIST_D_IS_OPEN)) { RetError(hotlist_get_selected_shape_r(list->data.directory_content, box, itemno, found, item_height)); } /* Follow the list */ list = list->next; } /* Finished */ return NULL; } /*************************************************/ /* hotlist_find_match() */ /* */ /* Takes a string from the given buffer and sees */ /* if there's something in the Hotlist that */ /* matches it in some way. */ /* */ /* If it finds something, it writes it back to */ /* the buffer and returns 1. */ /* */ /* Parameters: Pointer to the buffer holding the */ /* string to try and match; */ /* */ /* Size of the buffer in bytes. */ /* */ /* Returns: 1 if the buffer is updated with a */ /* match string, else 0 (buffer */ /* contents will be unaltered). */ /*************************************************/ int hotlist_find_match(char * buffer, int buffer_size) { hotlist_item * lowest_item = NULL; int lowest_offset = -1; int lowest_diff = 0; if (!buffer || !*buffer) return 0; hotlist_find_match_r(buffer, buffer_size, hotlist_root, &lowest_item, &lowest_offset, &lowest_diff); if (lowest_item && lowest_offset >= 0) { strncpy(buffer, lowest_item->data.url, buffer_size); buffer[buffer_size - 1] = 0; return 1; } return 0; } /*************************************************/ /* hotlist_find_match_r() */ /* */ /* Recursive back-end to hotlist_find_match. */ /* */ /* Parameters: Pointer to the buffer holding the */ /* string to try and match; */ /* */ /* Size of the buffer in bytes; */ /* */ /* Pointer to a hotlist_item at the */ /* top of the directory to scan; */ /* */ /* Pointer to a hotlist_item * in */ /* which the best match hotlist_item */ /* is written, if any; */ /* */ /* Pointer to an int, in which the */ /* lowest offset into a string so */ /* far is written; */ /* */ /* Pointer to an int, in which the */ /* lowest difference in lengths */ /* between the buffer and match */ /* string is written. */ /*************************************************/ static void hotlist_find_match_r(char * buffer, int buffer_size, hotlist_item * item, hotlist_item ** lowest_item, int * lowest_offset, int * lowest_diff) { while (item) { if (item->type == hl_directory) { hotlist_find_match_r(buffer, buffer_size, item->data.directory_content, lowest_item, lowest_offset, lowest_diff); } else { int this_offset = 0; int got_one = 0; const char * found; if (item->data.url) { found = strstr(item->data.url, buffer); if (found) { this_offset = found - item->data.url; got_one = 1; } } if (!got_one && item->name) { found = strstr(item->name, buffer); if (found) { this_offset = found - item->name; got_one = 1; } } if (got_one) { if (*lowest_offset < 0 || this_offset <= *lowest_offset) { int this_diff = strlen(item->data.url) - strlen(buffer); *lowest_offset = this_offset; if (this_offset < *lowest_offset) *lowest_diff = 0; if (!*lowest_diff || (this_diff && this_diff < *lowest_diff)) { *lowest_diff = this_diff; *lowest_item = item; } } } } item = item->next; } return; } /*************************************************/ /* hotlist_empty() */ /* */ /* See if the hotlist is empty or not. */ /* */ /* Returns: 1 if the hotlist is empty else 0. */ /*************************************************/ int hotlist_empty(void) { if ( !hotlist_root || !hotlist_root->data.directory_content ) return 1; return 0; }