diff options
author | Richard Wilson <rjw@netsurf-browser.org> | 2004-12-09 10:30:44 +0000 |
---|---|---|
committer | Richard Wilson <rjw@netsurf-browser.org> | 2004-12-09 10:30:44 +0000 |
commit | 32db7e04d0c3bd255b2e8aa7dbd7c2b884b35614 (patch) | |
tree | 312984b2b972916fd10e1efc338d08e437d55b70 | |
parent | 77a96712244ad4a8b4bde235aa051372a64a8789 (diff) | |
download | netsurf-32db7e04d0c3bd255b2e8aa7dbd7c2b884b35614.tar.gz netsurf-32db7e04d0c3bd255b2e8aa7dbd7c2b884b35614.tar.bz2 |
[project @ 2004-12-09 10:30:43 by rjw]
Re-implementation of hotlist via general tree code. Animations can be stopped once more. Purged a few xcalloc() calls.
svn path=/import/netsurf/; revision=1394
-rwxr-xr-x | !NetSurf/Resources/Sprites,ff9 | bin | 2996 -> 2140 bytes | |||
-rw-r--r-- | !NetSurf/Resources/de/Messages | 22 | ||||
-rw-r--r-- | !NetSurf/Resources/en/Messages | 28 | ||||
-rw-r--r-- | !NetSurf/Resources/fr/Messages | 22 | ||||
-rw-r--r-- | content/fetchcache.c | 5 | ||||
-rw-r--r-- | desktop/options.c | 376 | ||||
-rw-r--r-- | desktop/options.h | 6 | ||||
-rw-r--r-- | desktop/tree.c | 1113 | ||||
-rw-r--r-- | desktop/tree.h | 137 | ||||
-rw-r--r-- | gtk/gtk_treeview.c | 96 | ||||
-rw-r--r-- | image/gif.c | 53 | ||||
-rw-r--r-- | image/gif.h | 1 | ||||
-rw-r--r-- | image/gifread.c | 2 | ||||
-rw-r--r-- | image/mng.c | 4 | ||||
-rw-r--r-- | makefile | 11 | ||||
-rw-r--r-- | riscos/401login.c | 13 | ||||
-rw-r--r-- | riscos/gui.c | 123 | ||||
-rw-r--r-- | riscos/gui.h | 24 | ||||
-rw-r--r-- | riscos/help.c | 2 | ||||
-rw-r--r-- | riscos/hotlist.c | 2567 | ||||
-rw-r--r-- | riscos/menus.c | 307 | ||||
-rw-r--r-- | riscos/options.h | 3 | ||||
-rw-r--r-- | riscos/save.c | 7 | ||||
-rw-r--r-- | riscos/treeview.c | 1183 | ||||
-rw-r--r-- | riscos/treeview.h | 45 | ||||
-rw-r--r-- | riscos/wimp.c | 12 | ||||
-rw-r--r-- | riscos/wimp.h | 1 | ||||
-rw-r--r-- | riscos/window.c | 44 |
28 files changed, 3573 insertions, 2634 deletions
diff --git a/!NetSurf/Resources/Sprites,ff9 b/!NetSurf/Resources/Sprites,ff9 Binary files differindex abe992290..be6f0bf3d 100755 --- a/!NetSurf/Resources/Sprites,ff9 +++ b/!NetSurf/Resources/Sprites,ff9 diff --git a/!NetSurf/Resources/de/Messages b/!NetSurf/Resources/de/Messages index 9b6de49d2..c26fd02f1 100644 --- a/!NetSurf/Resources/de/Messages +++ b/!NetSurf/Resources/de/Messages @@ -119,9 +119,9 @@ Folder:Verzeichnis Links:Einträge Link:Eintrag SaveSelect:Sichern -Launch:Öffnen +Launch:Öffnen RETURN Edit:Bearbeiten -Delete:Löschen +Delete:Löschen ^X ResetUsage:Statistik zurücksetzen # Hotlist sub-window titles @@ -130,11 +130,17 @@ NewFolder:Verzeichnis anlegen EditLink:Eintrag bearbeiten EditFolder:Verzeichnis umbenennen -# Hotlist window -HotlistURL:URL: %s -HotlistAdded:eingetragen am: %s -HotlistLast:letzter Besuch: %s -HotlistVisits:Besuche gesamt: %i +# Default hotlist +HotlistHomepage:NetSurf homepage +HotlistTestBuild:NetSurf test builds + +# Tree URL text +TreeAdded:eingetragen am: %s +TreeLast:letzter Besuch: %s +TreeVisits:Besuche gesamt: %i +TreeUnknown:Unknown +TreeImport:Imported URL +TreeNewFolder:New directory # Download window Download:%s von %s %s/s noch %s @@ -372,7 +378,7 @@ HelpHotlist5:Klicken mit AUSWAHL markiert diesen Eintrag.|MDoppelklicken öffnet HelpHotlist6:Maustasten loslassen, um die Auswahl abzuschließen. HelpHotlist7:Maustasten loslassen, um das Verschieben auszuführen. -HelpHotToolbar0:Erzeugt neue Einträge oder Verzeichnisse.|MKlicken mit AUSWAHL erstellt ein neues Verzeichnis.|MKlicken mit SPEZIAL erstellt einen neuen Eintrag. +HelpHotToolbar0:Erzeugt neue Einträge oder Verzeichnisse.|MKlicken mit AUSWAHL erstellt ein neues Verzeichnis. HelpHotToolbar1:Löscht die markierten Einträge und Verzeichnisse. HelpHotToolbar2:Expandiert Einträge.|MKlicken mit AUSWAHL expandiert alle / alle markierten Einträge.|MKlicken mit SPEZIAL faltet alle / alle markierten Einträge zusammen.|MBei expandierten Einträgen werden zusätzliche Informationen angezeigt. HelpHotToolbar3:Öffnet Verzeichnisse.|MKlicken mit AUSWAHL öffnet alle / alle markierten Verzeichnisse.|MKlicken mit SPEZIAL schließt alle / alle markierten Verzeichnisse. diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages index dd3bb0b80..6ff639879 100644 --- a/!NetSurf/Resources/en/Messages +++ b/!NetSurf/Resources/en/Messages @@ -116,25 +116,31 @@ Collapse:Collapse All:All Folders:Directories Folder:Directory -Links:Entries -Link:Entry +Links:Addresses +Link:Address SaveSelect:Save -Launch:Launch +Launch:Launch RETURN Edit:Edit -Delete:Delete +Delete:Delete ^X ResetUsage:Reset statistics # Hotlist sub-window titles NewLink:Create new address NewFolder:Create new directory -EditLink:Edit entry +EditLink:Edit address EditFolder:Rename directory -# Hotlist window -HotlistURL:URL: %s -HotlistAdded:Added: %s -HotlistLast:Last visited: %s -HotlistVisits:Visits: %i +# Default hotlist +HotlistHomepage:NetSurf homepage +HotlistTestBuild:NetSurf test builds + +# Tree URL text +TreeAdded:Added: %s +TreeLast:Last visited: %s +TreeVisits:Visits: %i +TreeUnknown:Unknown +TreeImport:Imported URL +TreeNewFolder:New directory # Download window Download:%s of %s %s/s %s remaining @@ -372,7 +378,7 @@ HelpHotlist5:\Sselect this entry.|MDouble-click \s to launch this URL. HelpHotlist6:Release the mouse buttons to complete your selection. HelpHotlist7:Release the mouse buttons to move the selection. -HelpHotToolbar0:\Tcreate button.|M\Screate a new directory.|M\Acreate a new entry. +HelpHotToolbar0:\Tcreate button.|M\Screate a new directory. HelpHotToolbar1:\Tdelete button.|M\Sdelete the current selection. HelpHotToolbar2:\Texpand entries button.|M\Sexpand all entries or entries within the current selection.|M\Acollapse all entries or entries within the current selection.|MExpanded entries show additional details, such as a visit counter. HelpHotToolbar3:\Topen directories button.|M\Sopen all directories or directories within the current selection.|M\Aclose all directories or directories within the current selection. diff --git a/!NetSurf/Resources/fr/Messages b/!NetSurf/Resources/fr/Messages index aca7bb4ca..e2a9af949 100644 --- a/!NetSurf/Resources/fr/Messages +++ b/!NetSurf/Resources/fr/Messages @@ -119,9 +119,9 @@ Folder:Dossier Links:Adresses Link:Adresses SaveSelect:Sauver -Launch:Lancer +Launch:Lancer RETURN Edit:Éditer -Delete:Supprimer +Delete:Supprimer ^X ResetUsage:RAZ des statistiques # Hotlist sub-window titles @@ -130,11 +130,17 @@ NewFolder:Créer un nouveau dossier EditLink:Éditer l'adresse EditFolder:Éditer le dossier -# Hotlist window -HotlistURL:URL: %s -HotlistAdded:Ajoutée: %s -HotlistLast:Dernière visitée: %s -HotlistVisits:Visites: %i +# Default hotlist +HotlistHomepage:NetSurf homepage +HotlistTestBuild:NetSurf test builds + +# Tree URL text +TreeAdded:Ajoutée: %s +TreeLast:Dernière visitée: %s +TreeVisits:Visites: %i +TreeUnknown:Unknown +TreeImport:Imported URL +TreeNewFolder:New directory # Download window Download:%s de %s %s/s %s restants @@ -372,7 +378,7 @@ HelpHotlist5:\Ssélectionner cette entrée.|MDouble-cliquer \s pour lancer cette U HelpHotlist6:Lâcher les boutons de souris pour terminer votre sélection. HelpHotlist7:Lâcher les boutons de souris pour déplacer votre sélection. -HelpHotToolbar0:\Tle bouton Créer.|M\Scréer un nouveau répertoire.|M\Acréer une nouvelle entrée. +HelpHotToolbar0:\Tle bouton Créer.|M\Scréer un nouveau répertoire. HelpHotToolbar1:\Tle bouton Supprimer.|M\Ssupprimer la sélection courante. HelpHotToolbar2:\Tle bouton de déploiement des entrées.|M\Sdéployer toutes les entrées ou seulement celle de la sélection courante.|M\ARegrouper toutes les entrées ou seulement celles de la sélection courante.|MLes entrées déployées affichent des infos supplémentaires, comme un compteur de visite. HelpHotToolbar3:\Tle bouton d'ouverture de répertoires.|M\Souvrir tous les répertoires ou seulement ceux de la sélection courante.|M\Afermer tous les répertoires ou seulement ceux de la sélection courante. diff --git a/content/fetchcache.c b/content/fetchcache.c index 54fd7e1c8..192da6cd4 100644 --- a/content/fetchcache.c +++ b/content/fetchcache.c @@ -85,6 +85,11 @@ struct content * fetchcache(const char *url, if ((c = content_get(url1)) != NULL) { free(url1); content_add_user(c, callback, p1, p2); + /* resize HTML content to the required size */ + if (c->type == CONTENT_HTML) { + c->width = width; + c->height = height; + } return c; } } diff --git a/desktop/options.c b/desktop/options.c index a777f6d12..3ad212a14 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -5,6 +5,7 @@ * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk> * Copyright 2004 James Bursa <bursa@users.sourceforge.net> + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> */ /** \file @@ -14,11 +15,16 @@ * value is "0" or "1". */ +#include <assert.h> #include <stdbool.h> #include <stdio.h> #include <string.h> +#include "libxml/HTMLparser.h" +#include "libxml/HTMLtree.h" #include "netsurf/desktop/options.h" +#include "netsurf/desktop/tree.h" #include "netsurf/utils/log.h" +#include "netsurf/utils/messages.h" #include "netsurf/utils/utils.h" #ifdef riscos @@ -57,6 +63,9 @@ bool option_block_ads = false; int option_minimum_gif_delay = 10; /** Whether to send the referer HTTP header */ bool option_send_referer = true; +/** Whether to animate images */ +bool option_animate_images = true; + EXTRA_OPTION_DEFINE @@ -80,12 +89,21 @@ struct { { "block_advertisements", OPTION_BOOL, &option_block_ads }, { "minimum_gif_delay", OPTION_INTEGER, &option_minimum_gif_delay }, { "send_referer", OPTION_BOOL, &option_send_referer }, + { "animate_images", OPTION_BOOL, &option_animate_images }, \ EXTRA_OPTION_TABLE }; #define option_table_entries (sizeof option_table / sizeof option_table[0]) +static void options_load_hotlist_directory(xmlNode *ul, struct node *directory); +static void options_load_hotlist_entry(xmlNode *li, struct node *directory); +xmlNode *options_find_hotlist_element(xmlNode *node, const char *name); +bool options_save_hotlist_directory(struct node *directory, xmlNode *node); +bool options_save_hotlist_entry(struct node *entry, xmlNode *node); +bool options_save_hotlist_entry_comment(xmlNode *node, const char *name, int value); + + /** * Read options from a file. * @@ -201,3 +219,361 @@ void options_write(const char *path) fclose(fp); } + + +/** + * Loads a hotlist as a tree from a specified file. + * + * \param filename name of file to read + * \return the hotlist file represented as a tree, or NULL on failure + */ +struct tree *options_load_hotlist(const char *filename) { + xmlDoc *doc; + xmlNode *html, *body, *ul; + struct tree *tree; + + doc = htmlParseFile(filename, "iso-8859-1"); + if (!doc) { + warn_user("HotlistLoadError", messages_get("ParsingFail")); + return NULL; + } + + html = options_find_hotlist_element((xmlNode *) doc, "html"); + body = options_find_hotlist_element(html, "body"); + ul = options_find_hotlist_element(body, "ul"); + if (!ul) { + xmlFreeDoc(doc); + warn_user("HotlistLoadError", + "(<html>...<body>...<ul> not found.)"); + return NULL; + } + + tree = calloc(sizeof(struct tree), 1); + if (!tree) { + xmlFreeDoc(doc); + warn_user("NoMemory", 0); + return NULL; + } + tree->root = tree_create_folder_node(NULL, "Root"); + if (!tree->root) return NULL; + + options_load_hotlist_directory(ul, tree->root); + tree->root->expanded = true; + tree_initialise(tree); + + xmlFreeDoc(doc); + return tree; +} + + +/** + * Parse a directory represented as a ul. + * + * \param ul xmlNode for parsed ul + * \param directory directory to add this directory to + */ +void options_load_hotlist_directory(xmlNode *ul, struct node *directory) { + char *title; + struct node *dir; + xmlNode *n; + + assert(ul); + assert(directory); + + for (n = ul->children; n; n = n->next) { + /* The ul may contain entries as a li, or directories as + * an h4 followed by a ul. Non-element nodes may be present + * (eg. text, comments), and are ignored. */ + + if (n->type != XML_ELEMENT_NODE) + continue; + + if (strcmp(n->name, "li") == 0) { + /* entry */ + options_load_hotlist_entry(n, directory); + + } else if (strcmp(n->name, "h4") == 0) { + /* directory */ + title = (char *) xmlNodeGetContent(n); + if (!title) { + warn_user("HotlistLoadError", "(Empty <h4> " + "or memory exhausted.)"); + return; + } + + for (n = n->next; + n && n->type != XML_ELEMENT_NODE; + n = n->next) + ; + if (!n || strcmp(n->name, "ul") != 0) { + /* next element isn't expected ul */ + free(title); + warn_user("HotlistLoadError", "(Expected " + "<ul> not present.)"); + return; + } + + dir = tree_create_folder_node(directory, title); + if (!dir) + return; + options_load_hotlist_directory(n, dir); + } + } +} + + +/** + * Parse an entry represented as a li. + * + * \param li xmlNode for parsed li + * \param directory directory to add this entry to + */ +void options_load_hotlist_entry(xmlNode *li, struct node *directory) { + char *url = 0; + char *title = 0; + int filetype = 0xfaf; + int add_date = -1; + int last_date = -1; + int visits = 0; + char *comment; + struct node *entry; + xmlNode *n; + + for (n = li->children; n; n = n->next) { + /* The li must contain an "a" element, and may contain + * some additional data as comments. */ + + if (n->type == XML_ELEMENT_NODE && + strcmp(n->name, "a") == 0) { + url = (char *) xmlGetProp(n, (const xmlChar *) "href"); + title = (char *) xmlNodeGetContent(n); + + } else if (n->type == XML_COMMENT_NODE) { + comment = (char *) xmlNodeGetContent(n); + if (!comment) + continue; + if (strncmp("Type:", comment, 5) == 0) + filetype = atoi(comment + 5); + else if (strncmp("Added:", comment, 6) == 0) + add_date = atoi(comment + 6); + else if (strncmp("LastVisit:", comment, 10) == 0) + last_date = atoi(comment + 10); + else if (strncmp("Visits:", comment, 7) == 0) + visits = atoi(comment + 7); + } + } + + if (!url || !title) { + warn_user("HotlistLoadError", "(Missing <a> in <li> or " + "memory exhausted.)"); + return; + } + + entry = tree_create_URL_node(directory, title, url, filetype, add_date, + last_date, visits); +} + + +/** + * Search the children of an xmlNode for an element. + * + * \param node xmlNode to search children of, or 0 + * \param name name of element to find + * \return first child of node which is an element and matches name, or + * 0 if not found or parameter node is 0 + */ +xmlNode *options_find_hotlist_element(xmlNode *node, const char *name) { + xmlNode *n; + if (!node) + return 0; + for (n = node->children; + n && !(n->type == XML_ELEMENT_NODE && + strcmp(n->name, name) == 0); + n = n->next) + ; + return n; +} + + +/** + * Perform a save to a specified file + * + * /param filename the file to save to + */ +bool options_save_hotlist(struct tree *tree, const char *filename) { + int res; + xmlDoc *doc; + xmlNode *html, *head, *title, *body; + + /* Unfortunately the Browse Hotlist format is invalid HTML, + * so this is a lie. */ + doc = htmlNewDoc("http://www.w3.org/TR/html4/strict.dtd", + "-//W3C//DTD HTML 4.01//EN"); + if (!doc) { + warn_user("NoMemory", 0); + return false; + } + + html = xmlNewNode(NULL, "html"); + if (!html) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + xmlDocSetRootElement(doc, html); + + head = xmlNewChild(html, NULL, "head", NULL); + if (!head) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + title = xmlNewTextChild(head, NULL, "title", "NetSurf Hotlist"); + if (!title) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + body = xmlNewChild(html, NULL, "body", NULL); + if (!body) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + if (!options_save_hotlist_directory(tree->root, body)) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + doc->charset = XML_CHAR_ENCODING_UTF8; + res = htmlSaveFileEnc(filename, doc, "iso-8859-1"); + if (res == -1) { + warn_user("HotlistSaveError", 0); + xmlFreeDoc(doc); + return false; + } + + xmlFreeDoc(doc); + return true; +} + + +/** + * Add a directory to the HTML tree for saving. + * + * \param directory hotlist directory to add + * \param node node to add ul to + * \return true on success, false on memory exhaustion + */ +bool options_save_hotlist_directory(struct node *directory, xmlNode *node) { + struct node *child; + xmlNode *ul, *h4; + + ul = xmlNewChild(node, NULL, "ul", NULL); + if (!ul) + return false; + + for (child = directory->child; child; child = child->next) { + if (!child->folder) { + /* entry */ + if (!options_save_hotlist_entry(child, ul)) + return false; + } else { + /* directory */ + /* invalid HTML */ + h4 = xmlNewTextChild(ul, NULL, "h4", child->data.text); + if (!h4) + return false; + + if (!options_save_hotlist_directory(child, ul)) + return false; + } } + + return true; +} + + +/** + * Add an entry to the HTML tree for saving. + * + * The node must contain a sequence of node_elements in the following order: + * + * \param entry hotlist entry to add + * \param node node to add li to + * \return true on success, false on memory exhaustion + */ +bool options_save_hotlist_entry(struct node *entry, xmlNode *node) { + xmlNode *li, *a; + xmlAttr *href; + struct node_element *element; + + li = xmlNewChild(node, NULL, "li", NULL); + if (!li) + return false; + + a = xmlNewTextChild(li, NULL, "a", entry->data.text); + if (!a) + return false; + + element = tree_find_element(entry, TREE_ELEMENT_URL); + if (!element) + return false; + href = xmlNewProp(a, "href", element->text); + if (!href) + return false; + + if (element->user_data != 0xfaf) + if (!options_save_hotlist_entry_comment(li, + "Type", element->user_data)) + return false; + + element = tree_find_element(entry, TREE_ELEMENT_ADDED); + if ((element) && (element->user_data != -1)) + if (!options_save_hotlist_entry_comment(li, + "Added", element->user_data)) + return false; + + element = tree_find_element(entry, TREE_ELEMENT_LAST_VISIT); + if ((element) && (element->user_data != -1)) + if (!options_save_hotlist_entry_comment(li, + "LastVisit", element->user_data)) + return false; + + element = tree_find_element(entry, TREE_ELEMENT_VISITS); + if ((element) && (element->user_data != 0)) + if (!options_save_hotlist_entry_comment(li, + "Visits", element->user_data)) + return false; + return true; +} + + +/** + * Add a special comment node to the HTML tree for saving. + * + * \param node node to add comment to + * \param name name of special comment + * \param value value of special comment + * \return true on success, false on memory exhaustion + */ +bool options_save_hotlist_entry_comment(xmlNode *node, const char *name, int value) { + char s[40]; + xmlNode *comment; + + snprintf(s, sizeof s, "%s:%i", name, value); + s[sizeof s - 1] = 0; + + comment = xmlNewComment(s); + if (!comment) + return false; + if (!xmlAddChild(node, comment)) { + xmlFreeNode(comment); + return false; + } + + return true; +} diff --git a/desktop/options.h b/desktop/options.h index 0724f8c65..d811ffb7e 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -24,6 +24,8 @@ #ifndef _NETSURF_DESKTOP_OPTIONS_H_ #define _NETSURF_DESKTOP_OPTIONS_H_ +#include "netsurf/desktop/tree.h" + enum { OPTION_HTTP_PROXY_AUTH_NONE = 0, OPTION_HTTP_PROXY_AUTH_BASIC = 1, OPTION_HTTP_PROXY_AUTH_NTLM = 2 }; @@ -41,8 +43,12 @@ extern int option_memory_cache_size; extern bool option_block_ads; extern int option_minimum_gif_delay; extern bool option_send_referer; +extern bool option_animate_images; void options_read(const char *path); void options_write(const char *path); +struct tree *options_load_hotlist(const char *filename); +bool options_save_hotlist(struct tree *tree, const char *filename); + #endif diff --git a/desktop/tree.c b/desktop/tree.c new file mode 100644 index 000000000..d504ddcba --- /dev/null +++ b/desktop/tree.c @@ -0,0 +1,1113 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (implementation). + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "netsurf/desktop/tree.h" +#include "netsurf/desktop/options.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/utils.h" + +static void tree_recalculate_node(struct node *node, bool recalculate_sizes); +static void tree_recalculate_node_positions(struct node *root); +static void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y, + int clip_width, int clip_height); +static struct node_element *tree_create_node_element(struct node *parent, int user_type); +static int tree_get_node_width(struct node *node); +static int tree_get_node_height(struct node *node); +static void tree_reset_URL_node(struct tree *tree, struct node *node); +static void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x, int y, + int width, int height, bool invert); +static void tree_selected_to_processing(struct node *node); +struct node *tree_move_processing_node(struct node *node, struct node *link, bool before, + bool first); + +static int tree_initialising = 0; + + +/** + * Initialises a user-created tree + * + * \param tree the tree to initialise + */ +void tree_initialise(struct tree *tree) { + + assert(tree); + + tree_set_node_expanded(tree->root, true); + tree_initialise_nodes(tree->root); + tree_recalculate_node_positions(tree->root); + tree_set_node_expanded(tree->root, false); + tree->root->expanded = true; + tree_recalculate_node_positions(tree->root); + tree_recalculate_size(tree); +} + + +/** + * Initialises a user-created node structure + * + * \param root the root node to update from + */ +void tree_initialise_nodes(struct node *root) { + struct node *node; + + assert(root); + + tree_initialising++; + for (node = root; node; node = node->next) { + tree_recalculate_node(node, true); + if (node->child) { + tree_initialise_nodes(node->child); + } + } + tree_initialising--; + + if (tree_initialising == 0) + tree_recalculate_node_positions(root); +} + + +/** + * Recalculate the node data and redraw the relevant section of the tree. + * + * \param tree the tree to redraw + * \param node the node to update + * \param recalculate_sizes whether the elements have changed + * \param expansion the request is the result of a node expansion + */ +void tree_handle_node_changed(struct tree *tree, struct node *node, + bool recalculate_sizes, bool expansion) { + int width, height; + + assert(node); + + if ((expansion) && (node->expanded) && (node->child)) { + tree_set_node_expanded(node->child, false); + tree_set_node_selected(tree, node->child, false); + } + + width = node->box.width; + height = node->box.height; + if (recalculate_sizes) + tree_recalculate_node(node, true); + else if (expansion) + tree_recalculate_node(node, false); + if ((node->box.height != height) || (expansion)) { + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, node->box.y, 16384, 16384); + } else { + width = (width > node->box.width) ? width : node->box.width; + tree_redraw_area(tree, node->box.x, node->box.y, width, node->box.height); + } + if ((recalculate_sizes) || (expansion)) + tree_recalculate_size(tree); +} + + +/** + * Recalculate the node element and redraw the relevant section of the tree. + * The tree size is not updated. + * + * \param tree the tree to redraw + * \param element the node element to update + */ +void tree_handle_node_element_changed(struct tree *tree, struct node_element *element) { + int width, height; + + assert(element); + + width = element->box.width; + height = element->box.height; + tree_recalculate_node_element(element); + + if (element->box.height != height) { + tree_recalculate_node(element->parent, false); + tree_redraw_area(tree, 0, element->box.y, 16384, 16384); + } else { + if (element->box.width != width) + tree_recalculate_node(element->parent, false); + width = (width > element->box.width) ? width : + element->box.width; + tree_redraw_area(tree, element->box.x, element->box.y, width, element->box.height); + } +} + + +/** + * Recalculates the size of a node. + * + * \param node the node to update + * \param recalculate_sizes whether the node elements have changed + */ +void tree_recalculate_node(struct node *node, bool recalculate_sizes) { + struct node_element *element; + int width, height; + + assert(node); + + width = node->box.width; + height = node->box.height; + node->box.width = 0; + node->box.height = 0; + if (node->expanded) { + for (element = &node->data; element; element = element->next) { + if (recalculate_sizes) + tree_recalculate_node_element(element); + node->box.width = (node->box.width > + element->box.x + element->box.width - node->box.x) ? + node->box.width : + element->box.width + element->box.x - node->box.x; + node->box.height += element->box.height; + } + } else { + if (recalculate_sizes) + tree_recalculate_node_element(&node->data); + node->box.width = node->data.box.width; + node->box.height = node->data.box.height; + } + + if (height != node->box.height) { + for (; node->parent; node = node->parent); + if (tree_initialising == 0) + tree_recalculate_node_positions(node); + } +} + + +/** + * Recalculates the position of a node, its siblings and children. + * + * \param root the root node to update from + */ +void tree_recalculate_node_positions(struct node *root) { + struct node *parent; + struct node *node; + struct node *child; + struct node_element *element; + int y; + + for (node = root; node; node = node->next) { + if (node->previous) { + node->box.x = node->previous->box.x; + node->box.y = node->previous->box.y + + tree_get_node_height(node->previous); + } else if ((parent = node->parent)) { + node->box.x = parent->box.x + NODE_INSTEP; + node->box.y = parent->box.y + + parent->box.height; + for (child = parent->child; child != node; + child = child->next) + node->box.y += child->box.height; + } else { + node->box.x = 0; + node->box.y = -40; + } + if (node->expanded) { + if (node->folder) { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + tree_recalculate_node_positions(node->child); + } else { + y = node->box.y; + for (element = &node->data; element; + element = element->next) { + if (element->type == NODE_ELEMENT_TEXT_PLUS_SPRITE) { + element->box.x = node->box.x; + } else { + element->box.x = node->box.x + NODE_INSTEP; + } + element->box.y = y; + y += element->box.height; + } + } + } else { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + } + } +} + + +/** + * Calculates the width of a node including any children + * + * \param node the node to calculate the height of + * \return the total width of the node and children + */ +int tree_get_node_width(struct node *node) { + int width = 0; + int child_width; + + assert(node); + + for (; node; node = node->next) { + if (width < (node->box.x + node->box.width)) + width = node->box.x + node->box.width; + if ((node->child) && (node->expanded)) { + child_width = tree_get_node_width(node->child); + if (width < child_width) + width = child_width; + } + } + return width; +} + + +/** + * Calculates the height of a node including any children + * + * \param node the node to calculate the height of + * \return the total height of the node and children + */ +int tree_get_node_height(struct node *node) { + int y1; + + assert(node); + + if ((node->child) && (node->expanded)) { + y1 = node->box.y; + if (y1 < 0) + y1 = 0; + node = node->child; + while ((node->next) || ((node->child) && (node->expanded))) { + for (; node->next; node = node->next); + if ((node->child) && (node->expanded)) + node = node->child; + } + return node->box.y + node->box.height - y1; + } else { + return node->box.height; + } +} + + +/** + * Updates all siblinds and descendants of a node to an expansion state. + * No update is performed for the tree changes. + * + * \param node the node to set all siblings and descendants of + * \param expanded the expansion state to set + */ +void tree_set_node_expanded(struct node *node, bool expanded) { + for (; node; node = node->next) { + if (node->expanded != expanded) { + node->expanded = expanded; + tree_recalculate_node(node, false); + } + if ((node->child) && (node->expanded)) + tree_set_node_expanded(node->child, expanded); + } +} + + +/** + * Updates all siblinds and descendants of a node to an expansion state. + * + * \param tree the tree to update + * \param node the node to set all siblings and descendants of + * \param expanded the expansion state to set + * \param folder whether to update folders + * \param leaf whether to update leaves + * \return whether any changes were made + */ +bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded, bool folder, + bool leaf) { + struct node *entry = node; + bool redraw = false; + + for (; node; node = node->next) { + if ((node->expanded != expanded) && (node != tree->root) && + ((folder && (node->folder)) || (leaf && (!node->folder)))) { + node->expanded = expanded; + if (node->child) + tree_set_node_expanded(node->child, false); + tree_recalculate_node(node, false); + redraw = true; + } + if ((node->child) && (node->expanded)) + redraw |= tree_handle_expansion(tree, node->child, expanded, folder, leaf); + } + if ((entry == tree->root) && (redraw)) { + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); + tree_recalculate_size(tree); + } + return redraw; +} + + +/** + * Updates all siblinds and descendants of a node to an selected state. + * The required areas of the tree are redrawn. + * + * \param tree the tree to update nodes for + * \param node the node to set all siblings and descendants of + * \param selected the selection state to set + */ +void tree_set_node_selected(struct tree *tree, struct node *node, bool selected) { + for (; node; node = node->next) { + if ((node->selected != selected) && (node != tree->root)) { + node->selected = selected; + tree_redraw_area(tree, node->box.x, node->box.y, node->box.width, + node->data.box.height); + } + if ((node->child) && (node->expanded)) + tree_set_node_selected(tree, node->child, selected); + } +} + + +/** + * Finds a node at a specific location. + * + * \param root the root node to check from + * \param x the x co-ordinate + * \param y the y co-ordinate + * \param furniture whether the returned area was in an elements furniture + * \return the node at the specified position, or NULL for none + */ +struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture) { + struct node_element *result; + + if ((result = tree_get_node_element_at(root, x, y, furniture))) + return result->parent; + return NULL; +} + + +/** + * Finds a node element at a specific location. + * + * \param node the root node to check from + * \param x the x co-ordinate + * \param y the y co-ordinate + * \param furniture whether the returned area was in an elements furniture + * \return the node at the specified position, or NULL for none + */ +struct node_element *tree_get_node_element_at(struct node *node, int x, int y, + bool *furniture) { + struct node_element *element; + + *furniture = false; + for (; node; node = node->next) { + if (node->box.y > y) return NULL; + if ((node->box.x - NODE_INSTEP < x) && (node->box.y < y) && + (node->box.x + node->box.width >= x) && + (node->box.y + node->box.height >= y)) { + if (node->expanded) { + for (element = &node->data; element; + element = element->next) { + if ((element->box.x < x) && (element->box.y < y) && + (element->box.x + element->box.width >= x) && + (element->box.y + element->box.height >= y)) + return element; + } + } else if ((node->data.box.x < x) && + (node->data.box.y < y) && + (node->data.box.x + node->data.box.width >= x) && + (node->data.box.y + node->data.box.height >= y)) + return &node->data; + if (((node->child) || (node->data.next)) && + (node->data.box.x - NODE_INSTEP + 8 < x) && + (node->data.box.y + 8 < y) && + (node->data.box.x > x) && + (node->data.box.y + 32 > y)) { + *furniture = true; + return &node->data; + } + } + + if ((node->child) && (node->expanded) && + ((element = tree_get_node_element_at(node->child, x, y, + furniture)))) + return element; + } + return NULL; +} + + +/** + * Finds a node element from a node with a specific user_type + * + * \param node the node to examine + * \param user_type the user_type to check for + * \return the corresponding element + */ +struct node_element *tree_find_element(struct node *node, int user_type) { + struct node_element *element; + for (element = &node->data; element; element = element->next) + if (element->user_type == user_type) return element; + return NULL; +} + + +/** + * Moves nodes within a tree. + * + * \param tree the tree to process + * \param link the node to link before/as a child (folders) or before/after (link) + * \param before whether to link siblings before or after the supplied node + */ +void tree_move_selected_nodes(struct tree *tree, struct node *destination, bool before) { + struct node *link; + + tree_selected_to_processing(tree->root); + if (destination->processing) + return; + if ((destination->folder) && (!destination->expanded) && (!before)) { + destination->expanded = true; + tree_handle_node_changed(tree, destination, false, true); + } + link = tree_move_processing_node(tree->root, destination, before, true); + while (link) + link = tree_move_processing_node(tree->root, link, false, false); + + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); +} + + +/** + * Sets the processing flag to the selection state. + * + * \param node the node to process siblings and children of + */ +void tree_selected_to_processing(struct node *node) { + for (; node; node = node->next) { + node->processing = node->selected; + if (node->child) + tree_selected_to_processing(node->child); + } +} + + +/** + * Moves the first node in a tree with the processing flag set. + * + * \param tree the node to move siblings/children of + * \param link the node to link before/as a child (folders) or before/after (link) + * \param before whether to link siblings before or after the supplied node + * \param first whether to always link after the supplied node (ie not inside of folders) + * \return the node moved + */ +struct node *tree_move_processing_node(struct node *node, struct node *link, bool before, + bool first) { + struct node *result; + + bool folder = link->folder; + for (; node; node = node->next) { + if (node->processing) { + node->processing = false; + tree_delink_node(node); + if (!first) + link->folder = false; + tree_link_node(link, node, before); + if (!first) + link->folder = folder; + return node; + } + if (node->child) { + result = tree_move_processing_node(node->child, link, before, first); + if (result) + return result; + } + } + return NULL; +} + +/** + * Checks whether a node, its siblings or any children are selected. + * + * \param node the root node to check from + */ +bool tree_has_selection(struct node *node) { + for (; node; node = node->next) { + if (node->selected) + return true; + if ((node->child) && (node->expanded) && + (tree_has_selection(node->child))) + return true; + } + return false; +} + + +/** + * Updates the selected state for a region of nodes. + * + * \param tree the tree to update + * \param x the minimum x of the selection rectangle + * \param y the minimum y of the selection rectangle + * \param width the width of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state + */ +void tree_handle_selection_area(struct tree *tree, int x, int y, int width, int height, + bool invert) { + assert(tree); + assert(tree->root); + + if (!tree->root->child) return; + + if (width < 0) { + x += width; + width =- width; + } + if (height < 0) { + y += height; + height =- height; + } + + tree_handle_selection_area_node(tree, tree->root->child, x, y, width, height, invert); +} + + +/** + * Updates the selected state for a region of nodes. + * + * \param tree the tree to update + * \param node the node to update children and siblings of + * \param x the minimum x of the selection rectangle + * \param y the minimum y of the selection rectangle + * \param width the width of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state + */ +void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x, int y, + int width, int height, bool invert) { + + struct node_element *element; + struct node *update; + int x_max, y_max; + + assert(tree); + assert(node); + + x_max = x + width; + y_max = y + height; + + for (; node; node = node->next) { + if (node->box.y > y_max) return; + if ((node->box.x < x_max) && (node->box.y < y_max) && + (node->box.x + node->box.width + NODE_INSTEP >= x) && + (node->box.y + node->box.height >= y)) { + update = NULL; + if (node->expanded) { + for (element = &node->data; element; + element = element->next) { + if ((element->box.x < x_max) && (element->box.y < y_max) && + (element->box.x + element->box.width >= x) && + (element->box.y + element->box.height >= y)) { + update = element->parent; + break; + } + } + } else if ((node->data.box.x < x_max) && + (node->data.box.y < y_max) && + (node->data.box.x + node->data.box.width >= x) && + (node->data.box.y + node->data.box.height >= y)) + update = node->data.parent; + if ((update) && (node != tree->root)) { + if (invert) { + node->selected = !node->selected; + tree_handle_node_element_changed(tree, &node->data); + } else if (!node->selected) { + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + } + } + if ((node->child) && (node->expanded)) + tree_handle_selection_area_node(tree, node->child, x, y, width, height, + invert); + } +} + + +/** + * Redraws a tree. + * + * \param tree the tree to draw + * \param clip_x the minimum x of the clipping rectangle + * \param clip_y the minimum y of the clipping rectangle + * \param clip_width the width of the clipping rectangle + * \param clip_height the height of the clipping rectangle + */ +void tree_draw(struct tree *tree, int clip_x, int clip_y, int clip_width, + int clip_height) { + assert(tree); + assert(tree->root); + + if (!tree->root->child) return; + + tree_initialise_redraw(tree); + tree_draw_node(tree, tree->root->child, clip_x, + clip_y, clip_width, clip_height); +} + + +/** + * Redraws a node. + * + * \param tree the tree to draw + * \param node the node to draw children and siblings of + * \param clip_x the minimum x of the clipping rectangle + * \param clip_y the minimum y of the clipping rectangle + * \param clip_width the width of the clipping rectangle + * \param clip_height the height of the clipping rectangle + */ +void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y, + int clip_width, int clip_height) { + + struct node_element *element; + int x_max, y_max; + + assert(tree); + assert(node); + + x_max = clip_x + clip_width + NODE_INSTEP; + y_max = clip_y + clip_height; + + if ((node->parent->next) && (node->parent->next->box.y < clip_y)) + return; + + for (; node; node = node->next) { + if (node->box.y > y_max) return; + if (node->next) + tree_draw_line(tree, node->box.x - (NODE_INSTEP / 2), + node->box.y + (40 / 2), 0, + node->next->box.y - node->box.y); + if ((node->box.x < x_max) && (node->box.y < y_max) && + (node->box.x + node->box.width + NODE_INSTEP >= clip_x) && + (node->box.y + node->box.height >= clip_y)) { + if ((node->expanded) && (node->child)) + tree_draw_line(tree, node->box.x + (NODE_INSTEP / 2), + node->data.box.y + node->data.box.height, 0, + (40 / 2)); + tree_draw_line(tree, node->box.x - (NODE_INSTEP / 2), + node->data.box.y + + node->data.box.height - (40 / 2), + (NODE_INSTEP / 2) - 4, 0); + tree_draw_node_expansion(tree, node); + if (node->expanded) + for (element = &node->data; element; + element = element->next) + tree_draw_node_element(tree, element); + else + tree_draw_node_element(tree, &node->data); + } + if ((node->child) && (node->expanded)) + tree_draw_node(tree, node->child, clip_x, clip_y, clip_width, + clip_height); + } +} + + +/** + * Gets link characteristics to insert a node at a specified position. + * + * \param tree the tree to find link information for + * \param x the x co-ordinate + * \param y the y co-ordinate + * \param before set to whether the node should be linked before on exit + * \return the node to link with + */ +struct node *tree_get_link_details(struct tree *tree, int x, int y, bool *before) { + struct node *node = NULL; + bool furniture; + + assert(tree); + assert(tree->root); + + *before = false; + if (tree->root->child) + node = tree_get_node_at(tree->root->child, x, y, &furniture); + if ((!node) || (furniture)) + return tree->root; + + if (y < (node->box.y + (node->box.height / 2))) { + *before = true; + } else if ((node->folder) && (node->expanded) && (node->child)) { + node = node->child; + *before = true; + } + return node; +} + + +/** + * Links a node into the tree. + * + * \param link the node to link before/as a child (folders) or before/after (link) + * \param node the node to link + * \param before whether to link siblings before or after the supplied node + */ +void tree_link_node(struct node *link, struct node *node, bool before) { + struct node *sibling; + + assert(link); + assert(node); + + if ((!link->folder) || (before)) { + node->parent = link->parent; + if (before) { + node->next = link; + node->previous = link->previous; + if (link->previous) link->previous->next = node; + link->previous = node; + if ((link->parent) && (link->parent->child == link)) + link->parent->child = node; + } else { + node->previous = link; + node->next = link->next; + if (link->next) link->next->previous = node; + link->next = node; + } + } else { + sibling = link->child; + if (!sibling) { + link->child = node; + node->previous = NULL; + } else { + while (sibling->next) + sibling = sibling->next; + sibling->next = node; + node->previous = sibling; + } + node->parent = link; + node->next = NULL; + } +} + + +/** + * Delinks a node from the tree. + * + * \param node the node to delink + */ +void tree_delink_node(struct node *node) { + assert(node); + + if (node->parent) { + if (node->parent->child == node) + node->parent->child = node->next; + if (node->parent->child == NULL) + node->parent->expanded = false; + node->parent = NULL; + } + if (node->previous) + node->previous->next = node->next; + if (node->next) + node->next->previous = node->previous; + node->previous = NULL; + node->next = NULL; +} + + +/** + * Deletes all selected node from the tree. + * + * \param tree the tree to delete from + * \param node the node to delete + */ +void tree_delete_selected_nodes(struct tree *tree, struct node *node) { + struct node *next; + + while (node) { + next = node->next; + if ((node->selected) && (node != tree->root)) + tree_delete_node(tree, node, false); + if (node->child) + tree_delete_selected_nodes(tree, node->child); + node = next; + } +} + + +/** + * Deletes a node from the tree. + * + * \param tree the tree to delete from + * \param node the node to delete + * \param siblings whether to delete all siblings + */ +void tree_delete_node(struct tree *tree, struct node *node, bool siblings) { + struct node *next; + struct node *parent; + struct node_element *element; + + assert(node); + + while (node) { + next = node->next; + if (node->child) + tree_delete_node(tree, node->child, true); + parent = node->parent; + tree_delink_node(node); + for (element = &node->data; element; element = element->next) { + if (element->text) + free(element->text); + if (element->sprite) + free(element->sprite); /* \todo platform specific bits */ + } + while (node->data.next) { + element = node->data.next->next; + free(node->data.next); + node->data.next = element; + } + free(node); + + if (!siblings) + node = NULL; + else + node = next; + } + tree_recalculate_node_positions(tree->root); + tree_redraw_area(tree, 0, 0, 16384, 16384); /* \todo correct area */ + tree_recalculate_size(tree); +} + + +/** + * Creates a folder node with the specified title, and links it into the tree. + * + * \param parent the parent node, or NULL not to link + * \param title the node title (copied) + * \return the newly created node. + */ +struct node *tree_create_folder_node(struct node *parent, const char *title) { + struct node *node; + + assert(title); + + node = calloc(sizeof(struct node), 1); + if (!node) return NULL; + node->editable = true; + node->folder = true; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = squash_whitespace(title); + tree_set_node_sprite_folder(node); + if (parent) + tree_link_node(parent, node, false); + tree_recalculate_node(node, true); + return node; +} + + +/** + * Creates a leaf node with the specified title, and links it into the tree. + * + * \param parent the parent node, or NULL not to link + * \param title the node title (copied) + * \return the newly created node. + */ +struct node *tree_create_leaf_node(struct node *parent, const char *title) { + struct node *node; + + assert(title); + + node = calloc(sizeof(struct node), 1); + if (!node) return NULL; + node->folder = false; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = squash_whitespace(title); + if (parent) + tree_link_node(parent, node, false); + return node; +} + + +/** + * Creates a tree entry for a URL, and links it into the tree + * + * \param parent the node to link to + * \param title the node title + * \param url the node URL + * \param filetype the node filetype + * \param add_date the date added + * \param last_date the last visited date + * \param visits the number of visits + * \return the node created, or NULL for failure + */ +struct node *tree_create_URL_node(struct node *parent, const char *title, + const char *url, int filetype, int add_date, int last_date, int visits) { + struct node *node; + struct node_element *element; + + assert(title); + assert(url); + + node = tree_create_leaf_node(parent, title); + if (!node) + return NULL; + node->editable = true; + + element = tree_create_node_element(node, TREE_ELEMENT_URL); + if (element) { + element->user_data = filetype; + element->type = NODE_ELEMENT_TEXT; + element->text = squash_whitespace(url); + } + element = tree_create_node_element(node, TREE_ELEMENT_ADDED); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = add_date; + } + element = tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = last_date; + } + element = tree_create_node_element(node, TREE_ELEMENT_VISITS); + if (element) { + element->type = NODE_ELEMENT_TEXT; + element->user_data = visits; + } + + tree_update_URL_node(node); + + node->expanded = true; + tree_recalculate_node(node, true); + node->expanded = false; + + return node; +} + + +/** + * Resets all selected URL nodes from the tree. + * + * \param tree the tree to reset from + * \param node the node to reset + * \param selected whether to only reset selected nodes + */ +void tree_reset_URL_nodes(struct tree *tree, struct node *node, bool selected) { + for (; node; node = node->next) { + if (((node->selected) || (!selected)) && (!node->folder)) + tree_reset_URL_node(tree, node); + if (node->child) + tree_reset_URL_nodes(tree, node->child, + !((node->selected) | selected)); + } +} + + +/** + * Resets a tree entry for a URL + */ +void tree_reset_URL_node(struct tree *tree, struct node *node) { + struct node_element *element; + + assert(tree); + assert(node); + + element = tree_find_element(node, TREE_ELEMENT_LAST_VISIT); + if (element) + element->user_data = -1; + element = tree_find_element(node, TREE_ELEMENT_VISITS); + if (element) + element->user_data = 0; + tree_update_URL_node(node); + tree_recalculate_node(node, true); + + if (node->expanded) + tree_redraw_area(tree, node->box.x, node->box.y + node->data.box.height, + node->box.width, node->box.height - node->data.box.height); + +} + + +/** + * Creates an empty node element and links it to a node. + * + * \param parent the parent node + * \param user_type the required user_type + * \return the newly created element. + */ +struct node_element *tree_create_node_element(struct node *parent, int user_type) { + struct node_element *element; + struct node_element *link; + + assert(parent); + + element = calloc(sizeof(struct node_element), 1); + if (!element) return NULL; + element->parent = parent; + element->user_type = user_type; + + for (link = parent->data.next; ((link) && (link->user_type < user_type)); + link = link->next); + if (link) { + element->next = link->next; + link->next = element; + } else { + for (link = &parent->data; link->next; link = link->next); + link->next = element; + } + return element; +} + + +/** + * Recalculates the size of a tree. + * + * \param tree the tree to recalculate + */ +void tree_recalculate_size(struct tree *tree) { + int width, height; + + assert(tree); + + if (!tree->handle) + return; + width = tree->width; + height = tree->height; + tree->width = tree_get_node_width(tree->root); + tree->height = tree_get_node_height(tree->root); + if ((width != tree->width) || (height != tree->height)) + tree_resized(tree); +} + + +/** + * Returns the selected node, or NULL if multiple nodes are selected. + * + * \param node the node to search sibling and children + * \return the selected node, or NULL if multiple nodes are selected + */ +struct node *tree_get_selected_node(struct node *node) { + struct node *result = NULL; + struct node *temp; + + for (; node; node = node->next) { + if (node->selected) { + if (result) + return NULL; + result = node; + } + if ((node->child) && (node->expanded)) { + temp = tree_get_selected_node(node->child); + if (temp) { + if (result) + return NULL; + else + result = temp; + } + } + } + return result; +} diff --git a/desktop/tree.h b/desktop/tree.h new file mode 100644 index 000000000..632667ad0 --- /dev/null +++ b/desktop/tree.h @@ -0,0 +1,137 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (interface). + */ + +#ifndef _NETSURF_DESKTOP_TREE_H_ +#define _NETSURF_DESKTOP_TREE_H_ + +#include <stdbool.h> + +#define TREE_ELEMENT_URL 1 +#define TREE_ELEMENT_ADDED 2 +#define TREE_ELEMENT_LAST_VISIT 3 +#define TREE_ELEMENT_VISITS 4 + +#define NODE_INSTEP 40 + +struct node_sprite; + +typedef enum { + NODE_ELEMENT_TEXT, /* <-- Text only */ + NODE_ELEMENT_TEXT_PLUS_SPRITE, /* <-- Text and sprite */ + NODE_ELEMENT_SPRITE, /* <-- Sprite only */ +} node_element_type; + + +struct node_element_box { + int x; /* <-- X offset from origin */ + int y; /* <-- Y offset from origin */ + int width; /* <-- Element width */ + int height; /* <-- Element height */ +}; + + +struct node_element { + struct node *parent; /* <-- Parent node */ + node_element_type type; /* <-- Element type */ + struct node_element_box box; /* <-- Element bounding box */ + char *text; /* <-- Text for the element */ + struct node_sprite *sprite; /* <-- Sprite for the element */ + struct node_element *next; /* <-- Next node element */ + int user_data; /* <-- Private user data */ + int user_type; /* <-- Private user data */ +}; + + +struct node { + bool selected; /* <-- Whether the node is selected */ + bool expanded; /* <-- Whether the node is expanded */ + bool folder; /* <-- Whether the node is a folder */ + bool editable; /* <-- Whether the node is editable */ + bool processing; /* <-- Internal flag used when moving */ + struct node_element_box box; /* <-- Bounding box of all elements */ + struct node_element data; /* <-- Data to display */ + struct node *parent; /* <-- Parent entry (NULL for root) */ + struct node *child; /* <-- First child */ + struct node *previous; /* <-- Previous child of the parent */ + struct node *next; /* <-- Next child of the parent */ + +}; + +struct tree { + unsigned int handle; /* <-- User assigned handle */ + int offset_x; /* <-- User assigned tree x offset */ + int offset_y; /* <-- User assigned tree y offset */ + struct node *root; /* <-- Tree root element */ + int width; /* <-- Tree width */ + int height; /* <-- Tree height */ + int window_width; /* <-- Tree window width */ + int window_height; /* <-- Tree window height */ + int edit_handle; /* <-- Handle for editing information */ + bool movable; /* <-- Whether nodes can be moved */ + struct node_element *editing; /* <-- Node element being edited */ + char edit_buffer[256]; /* <-- Editing buffer */ + struct node *temp_selection; /* <-- Temporarily selected node */ +}; + + +/* Non-platform specific code */ +void tree_initialise(struct tree *tree); +void tree_initialise_nodes(struct node *root); +void tree_handle_node_changed(struct tree *tree, struct node *node, + bool recalculate_sizes, bool expansion); +void tree_handle_node_element_changed(struct tree *tree, + struct node_element *element); +struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture); +struct node_element *tree_get_node_element_at(struct node *node, int x, int y, + bool *furniture); +struct node_element *tree_find_element(struct node *node, int user_type); +void tree_move_selected_nodes(struct tree *tree, struct node *destination, + bool before); +bool tree_has_selection(struct node *node); +void tree_draw(struct tree *tree, int clip_x, int clip_y, int clip_width, + int clip_height); +void tree_link_node(struct node *link, struct node *node, bool before); +void tree_delink_node(struct node *node); +struct node *tree_create_folder_node(struct node *parent, const char *title); +struct node *tree_create_leaf_node(struct node *parent, const char *title); +void tree_set_node_sprite(struct node *node, const char *sprite, + const char *expanded); +void tree_set_node_sprite_folder(struct node *node); +struct node *tree_create_URL_node(struct node *parent, const char *title, + const char *url, int filetype, int add_date, int last_date, + int visits); +void tree_reset_URL_nodes(struct tree *tree, struct node *node, bool selected); +void tree_set_node_expanded(struct node *node, bool expanded); +void tree_set_node_selected(struct tree *tree, struct node *node, + bool selected); +void tree_handle_selection_area(struct tree *tree, int x, int y, int width, + int height, bool invert); +void tree_delete_selected_nodes(struct tree *tree, struct node *node); +void tree_delete_node(struct tree *tree, struct node *node, bool siblings); +void tree_recalculate_size(struct tree *tree); +bool tree_handle_expansion(struct tree *tree, struct node *node, bool expanded, + bool folder, bool leaf); +struct node *tree_get_selected_node(struct node *node); +struct node *tree_get_link_details(struct tree *tree, int x, int y, + bool *before); + + +/* Platform specific code */ +void tree_initialise_redraw(struct tree *tree); +void tree_redraw_area(struct tree *tree, int x, int y, int width, int height); +void tree_draw_line(struct tree *tree, int x, int y, int width, int height); +void tree_draw_node_element(struct tree *tree, struct node_element *element); +void tree_draw_node_expansion(struct tree *tree, struct node *node); +void tree_recalculate_node_element(struct node_element *element); +void tree_update_URL_node(struct node *node); +void tree_resized(struct tree *tree); + +#endif diff --git a/gtk/gtk_treeview.c b/gtk/gtk_treeview.c new file mode 100644 index 000000000..ced83dd1c --- /dev/null +++ b/gtk/gtk_treeview.c @@ -0,0 +1,96 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (implementation). + */ + + +#include "netsurf/desktop/tree.h" + + +/** + * Sets the origin variables to the correct values for a specified tree + * + * \param tree the tree to set the origin for + */ +void tree_initialise_redraw(struct tree *tree) { +} + + +/** + * Informs the current window manager that an area requires updating. + * + * \param tree the tree that is requesting a redraw + * \param x the x co-ordinate of the redraw area + * \param y the y co-ordinate of the redraw area + * \param width the width of the redraw area + * \param height the height of the redraw area + */ +void tree_redraw_area(struct tree *tree, int x, int y, int width, int height) { +} + + +/** + * Draws a line. + * + * \param tree the tree to draw a line for + * \param x the x co-ordinate + * \param x the y co-ordinate + * \param x the width of the line + * \param x the height of the line + */ +void tree_draw_line(struct tree *tree, int x, int y, int width, int height) { +} + + +/** + * Draws an element, including any expansion icons + * + * \param tree the tree to draw an element for + * \param element the element to draw + */ +void tree_draw_node_element(struct tree *tree, struct node_element *element) { +} + + +/** + * Draws an elements expansion icon + * + * \param tree the tree to draw the expansion for + * \param element the element to draw the expansion for + */ +void tree_draw_node_expansion(struct tree *tree, struct node *node) { +} + + +/** + * Recalculates the dimensions of a node element. + * + * \param element the element to recalculate + */ +void tree_recalculate_node_element(struct node_element *element) { +} + + +/** + * Updates the node details for a URL node. + * The internal node dimensions are not updated. + * + * \param node the node to update + */ +void tree_update_URL_node(struct node *node) { +} + + +/** + * Updates the tree owner following a tree resize + * + * \param tree the tree to update the owner of + */ +void tree_resized(struct tree *tree) { +} diff --git a/image/gif.c b/image/gif.c index 8fabf5033..be59c478c 100644 --- a/image/gif.c +++ b/image/gif.c @@ -41,15 +41,15 @@ static void nsgif_get_frame(struct content *c); bool nsgif_create(struct content *c, const char *params[]) { union content_msg_data msg_data; - /* Initialise our data structure - */ - c->data.gif.gif = calloc(sizeof(gif_animation), 1); - if (!c->data.gif.gif) { + /* Initialise our data structure + */ + c->data.gif.gif = calloc(sizeof(gif_animation), 1); + if (!c->data.gif.gif) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); warn_user("NoMemory", 0); - return false; - } + return false; + } c->data.gif.current_frame = 0; return true; } @@ -109,9 +109,8 @@ bool nsgif_convert(struct content *c, int iwidth, int iheight) { /* Schedule the animation if we have one */ - if (gif->frame_count > 1) { + if (gif->frame_count_partial > 1) schedule(gif->frames[0].frame_delay, nsgif_animate, c); - } /* Exit as a success */ @@ -126,11 +125,10 @@ bool nsgif_redraw(struct content *c, int x, int y, int clip_x0, int clip_y0, int clip_x1, int clip_y1, float scale, unsigned long background_colour) { - nsgif_get_frame(c); + if (c->data.gif.current_frame != c->data.gif.gif->decoded_frame) + nsgif_get_frame(c); c->bitmap = c->data.gif.gif->frame_image; - - return (c->data.gif.frame_drawn = plot.bitmap(x, y, width, height, - c->bitmap, background_colour)); + return plot.bitmap(x, y, width, height, c->bitmap, background_colour); } @@ -151,14 +149,16 @@ void nsgif_destroy(struct content *c) * \param c the content to update */ void nsgif_get_frame(struct content *c) { - int previous_frame, current_frame, frame; + int previous_frame, current_frame, frame; current_frame = c->data.gif.current_frame; - if (current_frame < c->data.gif.gif->decoded_frame) { + if (!option_animate_images) + current_frame = 0; + if (current_frame < c->data.gif.gif->decoded_frame) previous_frame = 0; - } else { + else previous_frame = c->data.gif.gif->decoded_frame + 1; - } + for (frame = previous_frame; frame <= current_frame; frame++) gif_decode_frame(c->data.gif.gif, frame); @@ -174,11 +174,11 @@ void nsgif_animate(void *p) struct content *c = p; union content_msg_data data; int delay; - + /* Advance by a frame, updating the loop count accordingly */ c->data.gif.current_frame++; - if (c->data.gif.current_frame == (int)c->data.gif.gif->frame_count) { + if (c->data.gif.current_frame == (int)c->data.gif.gif->frame_count_partial) { c->data.gif.current_frame = 0; /* A loop count of 0 has a special meaning of infinite @@ -186,29 +186,24 @@ void nsgif_animate(void *p) if (c->data.gif.gif->loop_count != 0) { c->data.gif.gif->loop_count--; if (c->data.gif.gif->loop_count == 0) { - c->data.gif.current_frame = c->data.gif.gif->frame_count - 1; + c->data.gif.current_frame = c->data.gif.gif->frame_count_partial - 1; c->data.gif.gif->loop_count = -1; } } } - /* Animate onwards if the previous frame was used (ie it's onscreen), if not - then we let the animation be performed by the redraw loop. By doing this - it minimises the delay in the plot cycle, resulting in less flicker. - */ - if (c->data.gif.frame_drawn) { - nsgif_get_frame(c); - c->data.gif.frame_drawn = false; - } - /* Continue animating if we should */ if (c->data.gif.gif->loop_count >= 0) { delay = c->data.gif.gif->frames[c->data.gif.current_frame].frame_delay; - if (delay < option_minimum_gif_delay) delay = option_minimum_gif_delay; + if (delay < option_minimum_gif_delay) + delay = option_minimum_gif_delay; schedule(delay, nsgif_animate, c); } + if (!option_animate_images) + return; + /* area within gif to redraw */ data.redraw.x = c->data.gif.gif->frames[c->data.gif.current_frame].redraw_x; data.redraw.y = c->data.gif.gif->frames[c->data.gif.current_frame].redraw_y; diff --git a/image/gif.h b/image/gif.h index db5b4b442..7c205ec3e 100644 --- a/image/gif.h +++ b/image/gif.h @@ -15,7 +15,6 @@ struct content; struct content_gif_data { struct gif_animation *gif; /**< GIF animation data */ int current_frame; /**< current frame to display [0...(max-1)] */ - bool frame_drawn; /**< set when current frame has been used */ }; bool nsgif_create(struct content *c, const char *params[]); diff --git a/image/gifread.c b/image/gifread.c index 77980ce25..8ca84d952 100644 --- a/image/gifread.c +++ b/image/gifread.c @@ -758,7 +758,7 @@ int gif_decode_frame(struct gif_animation *gif, unsigned int frame) { */ if ((background_action == 2) || (background_action == 3)) { gif->dirty_frame = frame; - LOG(("Dirty frame %i", gif->dirty_frame)); +// LOG(("Dirty frame %i", gif->dirty_frame)); } /* Initialise the LZW decoding diff --git a/image/mng.c b/image/mng.c index 008e8363f..a223c7fff 100644 --- a/image/mng.c +++ b/image/mng.c @@ -19,6 +19,7 @@ #include "netsurf/utils/config.h" #include "netsurf/content/content.h" #include "netsurf/desktop/browser.h" +#include "netsurf/desktop/options.h" #include "netsurf/desktop/plotters.h" #include "netsurf/image/bitmap.h" #include "netsurf/image/mng.h" @@ -387,7 +388,8 @@ bool nsmng_redraw(struct content *c, int x, int y, /* Check if we need to restart the animation */ - if (c->data.mng.waiting) nsmng_animate(c); + if ((c->data.mng.waiting) && (option_animate_images)) + nsmng_animate(c); return ret; } @@ -22,7 +22,7 @@ OBJECTS_COMMON += css.o css_enum.o parser.o ruleset.o scanner.o # css/ OBJECTS_COMMON += box.o form.o html.o html_redraw.o layout.o \ textplain.o # render/ OBJECTS_COMMON += messages.o pool.o translit.o url.o utils.o # utils/ -OBJECTS_COMMON += imagemap.o loginlist.o options.o # desktop/ +OBJECTS_COMMON += imagemap.o loginlist.o options.o tree.o # desktop/ OBJECTS_IMAGE = jpeg.o mng.o gif.o gifread.o # image/ @@ -33,8 +33,9 @@ OBJECTS_RISCOS += 401login.o bitmap.o buffer.o debugwin.o \ gui.o help.o history.o hotlist.o image.o \ menus.o mouseactions.o plotters.o plugin.o print.o \ save.o save_complete.o save_draw.o save_text.o \ - schedule.o search.o sprite.o textselection.o theme.o thumbnail.o \ - ufont.o uri.o url_protocol.o wimp.o window.o # riscos/ + schedule.o search.o sprite.o textselection.o theme.o \ + thumbnail.o treeview.o ufont.o uri.o url_protocol.o \ + wimp.o window.o # riscos/ # OBJECTS_RISCOS += memdebug.o OBJECTS_NCOS = $(OBJECTS_RISCOS) @@ -54,7 +55,7 @@ OBJECTS_GTK = $(OBJECTS_COMMON) $(OBJECTS_IMAGE) OBJECTS_GTK += filetyped.o # debug/ OBJECTS_GTK += browser.o netsurf.o version.o # desktop/ OBJECTS_GTK += font_pango.o gtk_bitmap.o gtk_gui.o \ - gtk_plotters.o gtk_window.o # gtk/ + gtk_plotters.o gtk_treeview.o gtk_window.o # gtk/ OBJDIR_RISCOS = arm-riscos-aof @@ -162,7 +163,7 @@ utils/translit.c: transtab # available), remove */*.[ch] from the line below. # Under RISC OS, you may require *Set UnixFS$sfix "", if perl gives # "No such file or directory" errors. -depend: */*.[ch] +depend: @echo "--> modified files $?" @echo "--> updating dependencies" @-mkdir -p $(OBJDIR_RISCOS) $(OBJDIR_NCOS) $(OBJDIR_DEBUG) $(OBJDIR_GTK) diff --git a/riscos/401login.c b/riscos/401login.c index b2f3cf4f0..c9fe9a895 100644 --- a/riscos/401login.c +++ b/riscos/401login.c @@ -27,9 +27,9 @@ static void get_unamepwd(void); static wimp_window *dialog_401_template; extern wimp_w dialog_401li; -static char *uname; +static char uname[256]; static char *url; -static char *pwd; +static char pwd[256]; static struct browser_window *bwin; @@ -66,8 +66,6 @@ void gui_401login_open(struct browser_window *bw, struct content *c, char *realm void ro_gui_401login_open(wimp_w parent, char *host, char* realm, char *fetchurl) { url = xstrdup(fetchurl); - uname = xcalloc(1, 256); - pwd = xcalloc(1, 256); uname[0] = pwd[0] = 0; /* fill in download window icons */ @@ -128,9 +126,12 @@ void ro_gui_401login_click(wimp_pointer *pointer) void get_unamepwd(void) { - char *lidets = xcalloc(strlen(uname)+strlen(pwd)+2, sizeof(char)); - if (lidets == NULL) + char *lidets = calloc(strlen(uname)+strlen(pwd)+2, sizeof(char)); + if (!lidets) { + LOG(("Insufficient memory for calloc")); + warn_user("NoMemory", 0); return; + } sprintf(lidets, "%s:%s", uname, pwd); diff --git a/riscos/gui.c b/riscos/gui.c index b8134589f..4b5856efb 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -1,7 +1,7 @@ /* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, - * http://www.opensource.org/licenses/gpl-license + * http://www.opensource.org/licenses/gpl-license * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net> * Copyright 2004 James Bursa <bursa@users.sourceforge.net> * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk> @@ -36,6 +36,7 @@ #include "netsurf/desktop/gui.h" #include "netsurf/desktop/netsurf.h" #include "netsurf/desktop/options.h" +#include "netsurf/desktop/tree.h" #include "netsurf/render/font.h" #include "netsurf/render/html.h" #include "netsurf/riscos/gui.h" @@ -49,6 +50,7 @@ #endif #include "netsurf/riscos/save_complete.h" #include "netsurf/riscos/theme.h" +#include "netsurf/riscos/treeview.h" #ifdef WITH_URI #include "netsurf/riscos/uri.h" #endif @@ -61,7 +63,7 @@ #include "netsurf/utils/utils.h" const char *__dynamic_da_name = "NetSurf"; /**< For UnixLib. */ -int __feature_imagefs_is_file = 1; /**< For UnixLib. */ +int __feature_imagefs_is_file = 1; /**< For UnixLib. */ /* default filename handling */ int __riscosify_control = __RISCOSIFY_NO_SUFFIX | __RISCOSIFY_NO_REVERSE_SUFFIX; @@ -84,11 +86,11 @@ bool gui_reformat_pending = false; gui_drag_type gui_current_drag_type; wimp_t task_handle; /**< RISC OS wimp task handle. */ static clock_t gui_last_poll; /**< Time of last wimp_poll. */ -osspriteop_area *gui_sprites; /**< Sprite area containing pointer and hotlist sprites */ +osspriteop_area *gui_sprites; /**< Sprite area containing pointer and hotlist sprites */ /** Accepted wimp user messages. */ static wimp_MESSAGE_LIST(34) task_messages = { { - message_HELP_REQUEST, + message_HELP_REQUEST, message_DATA_SAVE, message_DATA_SAVE_ACK, message_DATA_LOAD, @@ -184,7 +186,7 @@ void gui_init(int argc, char** argv) save_complete_init(); #endif - /* We don't have the universal boot sequence on NCOS */ + /* We don't have the universal boot sequence on NCOS */ #ifndef ncos options_read("Choices:WWW.NetSurf.Choices"); #else @@ -203,8 +205,8 @@ void gui_init(int argc, char** argv) default_stylesheet_url = strdup("file:/<NetSurf$Dir>/Resources/CSS"); adblock_stylesheet_url = strdup("file:/<NetSurf$Dir>/Resources/AdBlock"); - /* Totally pedantic, but base the taskname on the build options. - */ + /* Totally pedantic, but base the taskname on the build options. + */ #ifndef ncos error = xwimp_initialise(wimp_VERSION_RO38, "NetSurf", (const wimp_message_list *) &task_messages, 0, @@ -220,7 +222,7 @@ void gui_init(int argc, char** argv) die(error->errmess); } - /* We don't need to check the fonts on NCOS */ + /* We don't need to check the fonts on NCOS */ #ifndef ncos ro_gui_check_fonts(); #endif @@ -231,11 +233,11 @@ void gui_init(int argc, char** argv) xwimp_start_task("Desktop", 0); /* Load our chosen theme - */ - ro_gui_theme_initialise(); - descriptor = ro_gui_theme_find(option_theme); - if (!descriptor) descriptor = ro_gui_theme_find("NetSurf"); - ro_gui_theme_apply(descriptor); + */ + ro_gui_theme_initialise(); + descriptor = ro_gui_theme_find(option_theme); + if (!descriptor) descriptor = ro_gui_theme_find("NetSurf"); + ro_gui_theme_apply(descriptor); /* Open the templates */ @@ -258,9 +260,10 @@ void gui_init(int argc, char** argv) ro_gui_history_init(); wimp_close_template(); ro_gui_sprites_init(); - ro_gui_hotlist_init(); + ro_gui_tree_initialise(); /* must be done after sprite loading */ + ro_gui_hotlist_initialise(); - /* We don't create an Iconbar icon on NCOS */ + /* We don't create an Iconbar icon on NCOS */ #ifndef ncos ro_gui_icon_bar_create(); #endif @@ -519,7 +522,7 @@ void gui_init2(int argc, char** argv) void gui_quit(void) { ro_gui_window_quit(); - ro_gui_hotlist_save(); + ro_gui_hotlist_save(); ro_gui_history_quit(); free(gui_sprites); xwimp_close_down(task_handle); @@ -686,10 +689,21 @@ void gui_multitask(void) void ro_gui_poll_queue(wimp_event_no event, wimp_block *block) { struct ro_gui_poll_block *q = - xcalloc(1, sizeof(struct ro_gui_poll_block)); + calloc(1, sizeof(struct ro_gui_poll_block)); + if (!q) { + LOG(("Insufficient memory for calloc")); + warn_user("NoMemory", 0); + return; + } q->event = event; - q->block = xcalloc(1, sizeof(*block)); + q->block = calloc(1, sizeof(*block)); + if (!q->block) { + free(q); + LOG(("Insufficient memory for calloc")); + warn_user("NoMemory", 0); + return; + } memcpy(q->block, block, sizeof(*block)); q->next = NULL; @@ -746,8 +760,8 @@ void ro_gui_redraw_window_request(wimp_draw *redraw) if (redraw->w == history_window) ro_gui_history_redraw(redraw); - else if (redraw->w == hotlist_window) - ro_gui_hotlist_redraw(redraw); + else if ((hotlist_tree) && (redraw->w == (wimp_w)hotlist_tree->handle)) + ro_gui_tree_redraw(redraw, hotlist_tree); else if ((hotlist_toolbar) && (hotlist_toolbar->toolbar_handle == redraw->w)) ro_gui_theme_redraw(hotlist_toolbar, redraw); else if (redraw->w == dialog_debug) @@ -757,7 +771,7 @@ void ro_gui_redraw_window_request(wimp_draw *redraw) else if ((g = ro_gui_toolbar_lookup(redraw->w)) != NULL) ro_gui_theme_redraw(g->toolbar, redraw); else { - ro_gui_dialog_redraw(redraw); + ro_gui_dialog_redraw(redraw); } } @@ -774,6 +788,8 @@ void ro_gui_open_window_request(wimp_open *open) g = ro_gui_window_lookup(open->w); if (g) { ro_gui_window_open(g, open); + } else if ((hotlist_tree) && (open->w == (wimp_w)hotlist_tree->handle)){ + ro_gui_tree_open(open, hotlist_tree); } else { error = xwimp_open_window(open); if (error) { @@ -861,7 +877,7 @@ void ro_gui_mouse_click(wimp_pointer *pointer) ro_gui_icon_bar_click(pointer); else if (pointer->w == history_window) ro_gui_history_click(pointer); - else if (pointer->w == hotlist_window) + else if ((hotlist_tree) && (pointer->w == (wimp_w)hotlist_tree->handle)) ro_gui_hotlist_click(pointer); else if (pointer->w == dialog_saveas) ro_gui_save_click(pointer); @@ -937,12 +953,12 @@ void ro_gui_drag_end(wimp_dragged *drag) case GUI_DRAG_STATUS_RESIZE: break; - case GUI_DRAG_HOTLIST_SELECT: - ro_gui_hotlist_selection_drag_end(drag); + case GUI_DRAG_TREE_SELECT: + ro_gui_tree_selection_drag_end(drag); break; - case GUI_DRAG_HOTLIST_MOVE: - ro_gui_hotlist_move_drag_end(drag); + case GUI_DRAG_TREE_MOVE: + ro_gui_tree_move_drag_end(drag); break; } } @@ -958,13 +974,13 @@ void ro_gui_keypress(wimp_key *key) struct gui_window *g; os_error *error; - if (key->w == hotlist_window) + if ((hotlist_tree) && (key->w == (wimp_w)hotlist_tree->handle)) { handled = ro_gui_hotlist_keypress(key->c); - else if ((g = ro_gui_window_lookup(key->w)) != NULL) + } else if ((g = ro_gui_window_lookup(key->w)) != NULL) handled = ro_gui_window_keypress(g, key->c, false); else if ((g = ro_gui_toolbar_lookup(key->w)) != NULL) handled = ro_gui_window_keypress(g, key->c, true); - else + else handled = ro_gui_dialog_keypress(key); if (!handled) { @@ -1021,9 +1037,10 @@ void ro_gui_user_message(wimp_event_no event, wimp_message *message) &message->data); break; case message_MENUS_DELETED: - if (current_menu == hotlist_menu) { + if ((current_menu == hotlist_menu) && (hotlist_tree)) ro_gui_hotlist_menu_closed(); - } + current_menu = NULL; + current_gui = NULL; break; case message_MODE_CHANGE: ro_gui_history_mode_change(); @@ -1127,10 +1144,13 @@ void ro_msg_dataload(wimp_message *message) int file_type = message->data.data_xfer.file_type; char *url = 0; struct gui_window *g; + struct node *node; + struct node *link; os_error *error; + int x, y; + bool before; g = ro_gui_window_lookup(message->data.data_xfer.w); - if (g && ro_gui_window_dataload(g, message)) return; @@ -1152,6 +1172,35 @@ void ro_msg_dataload(wimp_message *message) else return; + + if (!url) + /* error has already been reported by one of the three + * functions called above */ + return; + + if (g) { + browser_window_go(g->bw, url, 0); + } else { + if ((hotlist_tree) && ((wimp_w)hotlist_tree->handle == + message->data.data_xfer.w)) { + ro_gui_tree_get_tree_coordinates(hotlist_tree, + message->data.data_xfer.pos.x, + message->data.data_xfer.pos.y, + &x, &y); + link = tree_get_link_details(hotlist_tree, x, y, &before); + node = tree_create_URL_node(NULL, + messages_get("TreeImport"), url, file_type, + time(NULL), -1, 0); + tree_link_node(link, node, before); + tree_handle_node_changed(hotlist_tree, node, false, true); + tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, 0, + NODE_INSTEP, 16384); + ro_gui_tree_start_edit(hotlist_tree, &node->data, NULL); + } else { + browser_window_create(url, 0, 0); + } + } + /* send DataLoadAck */ message->action = message_DATA_LOAD_ACK; message->your_ref = message->my_ref; @@ -1163,16 +1212,6 @@ void ro_msg_dataload(wimp_message *message) return; } - if (!url) - /* error has already been reported by one of the three - * functions called above */ - return; - - if (g) - browser_window_go(g->bw, url, 0); - else - browser_window_create(url, 0, 0); - free(url); } diff --git a/riscos/gui.h b/riscos/gui.h index c4d55f29a..bf5db9b85 100644 --- a/riscos/gui.h +++ b/riscos/gui.h @@ -19,6 +19,7 @@ #include "netsurf/desktop/netsurf.h" #include "netsurf/desktop/gui.h" #include "netsurf/desktop/options.h" +#include "netsurf/desktop/tree.h" #define THEMES_DIR "<NetSurf$Dir>.Themes" @@ -31,7 +32,6 @@ extern wimp_w dialog_info, dialog_saveas, dialog_config, dialog_config_br, dialog_config_th_pane, dialog_debug, dialog_folder, dialog_entry, dialog_search, dialog_print, dialog_config_font; extern wimp_w history_window; -extern wimp_w hotlist_window; extern wimp_menu *iconbar_menu, *browser_menu, *combo_menu, *hotlist_menu, *proxyauth_menu, *languages_menu, *toolbar_menu, *image_quality_menu; @@ -44,6 +44,7 @@ extern osspriteop_area *gui_sprites; extern struct toolbar *hotlist_toolbar; extern bool dialog_folder_add, dialog_entry_add, hotlist_insert; extern bool print_active, print_text_black; +extern struct tree *hotlist_tree; typedef enum { GUI_SAVE_SOURCE, @@ -60,7 +61,7 @@ typedef enum { typedef enum { GUI_DRAG_SELECTION, GUI_DRAG_DOWNLOAD_SAVE, GUI_DRAG_SAVE, GUI_DRAG_STATUS_RESIZE, - GUI_DRAG_HOTLIST_SELECT, GUI_DRAG_HOTLIST_MOVE } gui_drag_type; + GUI_DRAG_TREE_SELECT, GUI_DRAG_TREE_MOVE } gui_drag_type; extern gui_drag_type gui_current_drag_type; @@ -86,7 +87,6 @@ struct gui_window { /** Options. */ struct { float scale; /**< Scale, 1.0 = 100%. */ - bool animate_images; /**< Animations should run. */ bool background_images; /**< Display background images. */ bool background_blending; /**< Perform background blending on text. */ bool buffer_animations; /**< Use screen buffering for animations. */ @@ -203,26 +203,16 @@ void ro_gui_history_click(wimp_pointer *pointer); void ro_gui_history_mouse_at(wimp_pointer *pointer); /* in hotlist.c */ -void ro_gui_hotlist_init(void); +void ro_gui_hotlist_initialise(void); void ro_gui_hotlist_save(void); void ro_gui_hotlist_show(void); -void ro_gui_hotlist_add(char *title, struct content *content); -void ro_gui_hotlist_redraw(wimp_draw *redraw); void ro_gui_hotlist_click(wimp_pointer *pointer); -void ro_gui_hotlist_selection_drag_end(wimp_dragged *drag); -void ro_gui_hotlist_move_drag_end(wimp_dragged *drag); bool ro_gui_hotlist_keypress(int key); -void ro_gui_hotlist_menu_closed(void); void ro_gui_hotlist_toolbar_click(wimp_pointer* pointer); -int ro_gui_hotlist_get_selected(bool folders); -void ro_gui_hotlist_reset_statistics(void); -void ro_gui_hotlist_set_selected(bool selected); -void ro_gui_hotlist_set_expanded(bool expand, bool folders, bool links); -void ro_gui_hotlist_delete_selected(void); -void ro_gui_hotlist_save_as(const char *file); -void ro_gui_hotlist_prepare_folder_dialog(bool selected); -void ro_gui_hotlist_prepare_entry_dialog(bool selected); +void ro_gui_hotlist_prepare_folder_dialog(struct node *node); +void ro_gui_hotlist_prepare_entry_dialog(struct node *node); void ro_gui_hotlist_dialog_click(wimp_pointer *pointer); +void ro_gui_hotlist_menu_closed(void); int ro_gui_hotlist_help(int x, int y); /* in save.c */ diff --git a/riscos/help.c b/riscos/help.c index 22fc5b09b..b6d7c0bb3 100644 --- a/riscos/help.c +++ b/riscos/help.c @@ -119,7 +119,7 @@ void ro_gui_interactive_help_request(wimp_message *message) { sprintf(message_token, "HelpHotFolder%i", (int)icon); } else if (window == dialog_entry) { sprintf(message_token, "HelpHotEntry%i", (int)icon); - } else if (window == hotlist_window) { + } else if ((hotlist_tree) && (window == (wimp_w)hotlist_tree->handle)) { sprintf(message_token, "HelpHotlist%i", ro_gui_hotlist_help(message_data->pos.x, message_data->pos.y)); diff --git a/riscos/hotlist.c b/riscos/hotlist.c index 021c5c365..055b71190 100644 --- a/riscos/hotlist.c +++ b/riscos/hotlist.c @@ -11,6 +11,7 @@ #include <string.h> #include <stdbool.h> +#include <stdio.h> #include <stdlib.h> #include <time.h> #include <swis.h> @@ -22,102 +23,20 @@ #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "netsurf/content/content.h" +#include "netsurf/desktop/tree.h" #include "netsurf/riscos/gui.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/tinct.h" +#include "netsurf/riscos/treeview.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" #include "netsurf/utils/utils.h" #include "netsurf/utils/url.h" -#define HOTLIST_EXPAND 0 -#define HOTLIST_COLLAPSE 1 -#define HOTLIST_ENTRY 2 -#define HOTLIST_LINE 3 -#define HOTLIST_TLINE 4 -#define HOTLIST_BLINE 5 - -#define HOTLIST_TEXT_BUFFER 256 - -#define HOTLIST_LEAF_INSET 32 -#define HOTLIST_ICON_WIDTH 36 -#define HOTLIST_LINE_HEIGHT 44 -#define HOTLIST_TEXT_PADDING 16 - -struct hotlist_entry { - - /** The next hotlist entry at this level, or NULL for no more - */ - struct hotlist_entry *next_entry; - - /** The child hotlist entry (NULL for no children). - The children value must be set for this value to take effect. - */ - struct hotlist_entry *child_entry; - - /** The hotlist entry that has this entry as its next entry - */ - struct hotlist_entry *previous_entry; - - /** The hotlist entry that this is a child of - */ - struct hotlist_entry *parent_entry; - - /** The number of children (-1 for non-folders, >=0 for folders) - */ - int children; - - /** The title of the hotlist entry/folder, UTF-8. - */ - char *title; - - /** The URL of the hotlist entry (NULL for folders) - */ - char *url; - - /** Whether this entry is expanded - */ - bool expanded; - - /** Whether this entry is selected - */ - bool selected; - - /** The content filetype (not for folders) - */ - int filetype; - - /** The number of visits - */ - int visits; - - /** Add/last visit dates - */ - time_t add_date; - time_t last_date; - - /** Position on last reformat (relative to window origin) - */ - int x0; - int y0; - int width; - int height; - - /** Cached values - */ - int collapsed_width; - int expanded_width; - - /** The width of the various lines sub-text - */ - int widths[4]; - - /** Whether the item is awaiting processing - */ - bool process; -}; +static void ro_gui_hotlist_visited(struct content *content, struct tree *tree, + struct node *node); /* A basic window for the toolbar and status */ @@ -129,7 +48,8 @@ static wimp_window hotlist_window_definition = { wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | wimp_WINDOW_TOGGLE_ICON | wimp_WINDOW_SIZE_ICON | - wimp_WINDOW_VSCROLL, + wimp_WINDOW_VSCROLL | wimp_WINDOW_IGNORE_XEXTENT | + wimp_WINDOW_IGNORE_YEXTENT, wimp_COLOUR_BLACK, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_LIGHT_GREY, @@ -138,158 +58,42 @@ static wimp_window hotlist_window_definition = { wimp_COLOUR_MID_LIGHT_GREY, wimp_COLOUR_CREAM, 0, - {0, -800, 16384, 0}, + {0, -16384, 16384, 0}, wimp_ICON_TEXT | wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED, wimp_BUTTON_DOUBLE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT, wimpspriteop_AREA, 1, - 256, + 100, {""}, - 0 + 0, + {} }; -/* An icon to plot text with -*/ -static wimp_icon text_icon; -static wimp_icon sprite_icon; - -/* Temporary workspace for plotting -*/ -static char drag_name[12]; -static char icon_name[12]; -static char extended_text[HOTLIST_TEXT_BUFFER]; - -/* Whether a reformat is pending -*/ -static bool reformat_pending = false; -static int max_width = 0; -static int max_height = 0; /* The hotlist window, toolbar and plot origins */ -wimp_w hotlist_window; -struct toolbar *hotlist_toolbar = NULL; -static int origin_x, origin_y; - -/* The current redraw rectangle -*/ -static int clip_x0, clip_y0, clip_x1, clip_y1; - -/* The root entry -*/ -static struct hotlist_entry root; - -/* The sprite header addresses for Tinct -*/ -static char *sprite[6]; - -/* The drag buttons -*/ -bool dragging; -wimp_mouse_state drag_buttons; - -/* Whether the current selection was from a menu click -*/ -bool menu_selection = false; -bool menu_open = false; +static wimp_w hotlist_window; +struct toolbar *hotlist_toolbar; +struct tree *hotlist_tree; /* Whether the editing facilities are for add so that we know how to reset the dialog boxes on a adjust-cancel and the action to perform on ok. */ -bool dialog_folder_add = false; -bool dialog_entry_add = false; -bool hotlist_insert = false; - +struct node *dialog_folder_node; +struct node *dialog_entry_node; -static bool ro_gui_hotlist_initialise_sprite(const char *name, int number); -static bool ro_gui_hotlist_load(void); -static void ro_gui_hotlist_load_file(const char *filename); -static void ro_gui_hotlist_load_directory(xmlNode *ul, - struct hotlist_entry *directory); -static void ro_gui_hotlist_load_entry(xmlNode *li, - struct hotlist_entry *directory); -xmlNode *ro_gui_hotlist_find_element(xmlNode *node, const char *name); -bool ro_gui_hotlist_save_directory(struct hotlist_entry *directory, - xmlNode *node); -bool ro_gui_hotlist_save_entry(struct hotlist_entry *entry, - xmlNode *node); -bool ro_gui_hotlist_save_entry_comment(xmlNode *node, - const char *name, int value); -static void ro_gui_hotlist_link_entry(struct hotlist_entry *link, struct hotlist_entry *entry, bool before); -static void ro_gui_hotlist_delink_entry(struct hotlist_entry *entry); -static void ro_gui_hotlist_delete_entry(struct hotlist_entry *entry, bool siblings); -static void ro_gui_hotlist_visited_update(struct content *content, struct hotlist_entry *entry); -static int ro_gui_hotlist_redraw_tree(struct hotlist_entry *entry, int level, int x0, int y0); -static int ro_gui_hotlist_redraw_item(struct hotlist_entry *entry, int level, int x0, int y0); -static struct hotlist_entry *ro_gui_hotlist_create_entry(const char *title, const char *url, - int filetype, struct hotlist_entry *folder); -static void ro_gui_hotlist_update_entry_size(struct hotlist_entry *entry); -static struct hotlist_entry *ro_gui_hotlist_find_entry(int x, int y, struct hotlist_entry *entry); -static int ro_gui_hotlist_selection_state(struct hotlist_entry *entry, bool selected, bool redraw); -static void ro_gui_hotlist_selection_drag(struct hotlist_entry *entry, - int x0, int y0, int x1, int y1, - bool toggle, bool redraw); -static int ro_gui_hotlist_selection_count(struct hotlist_entry *entry, bool folders); -static void ro_gui_hotlist_update_expansion(struct hotlist_entry *entry, bool only_selected, - bool folders, bool links, bool expand, bool contract); -static void ro_gui_hotlist_launch_selection(struct hotlist_entry *entry); -static void ro_gui_hotlist_invalidate_statistics(struct hotlist_entry *entry); -static struct hotlist_entry *ro_gui_hotlist_first_selection(struct hotlist_entry *entry); -static void ro_gui_hotlist_selection_to_process(struct hotlist_entry *entry); -static bool ro_gui_hotlist_move_processing(struct hotlist_entry *entry, struct hotlist_entry *destination, bool before); - -#define hotlist_ensure_sprite(buffer, fallback) if (xwimpspriteop_read_sprite_info(buffer, 0, 0, 0, 0)) sprintf(buffer, fallback) -#define hotlist_redraw_entry(entry, full) xwimp_force_redraw(hotlist_window, full ? 0 : entry->x0, \ - full ? -16384 : entry->y0, full ? 16384 : entry->x0 + entry->expanded_width, entry->y0 + entry->height); -#define hotlist_redraw_entry_title(entry) xwimp_force_redraw(hotlist_window, entry->x0, \ - entry->y0 + entry->height - HOTLIST_LINE_HEIGHT, entry->x0 + entry->width, entry->y0 + entry->height); - - - -void ro_gui_hotlist_init(void) { +void ro_gui_hotlist_initialise(void) { + FILE *fp; const char *title; - os_box extent = {0, 0, 0, 0}; os_error *error; - - /* Set the initial root options - */ - root.next_entry = NULL; - root.child_entry = NULL; - root.children = 0; - root.expanded = true; - - /* Load the hotlist - */ - if (!ro_gui_hotlist_load()) return; - - /* Get our sprite ids for faster plotting. - */ - if (ro_gui_hotlist_initialise_sprite("expand", HOTLIST_EXPAND) || - ro_gui_hotlist_initialise_sprite("collapse", HOTLIST_COLLAPSE) || - ro_gui_hotlist_initialise_sprite("entry", HOTLIST_ENTRY) || - ro_gui_hotlist_initialise_sprite("line", HOTLIST_LINE) || - ro_gui_hotlist_initialise_sprite("halflinet", HOTLIST_TLINE) || - ro_gui_hotlist_initialise_sprite("halflineb", HOTLIST_BLINE)) { - return; - } - - /* Update our text icon - */ - text_icon.data.indirected_text.validation = (char *) -1; - text_icon.data.indirected_text.size = 256; - sprite_icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED | - wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | - (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | - (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); - sprite_icon.data.indirected_sprite.area = wimpspriteop_AREA; - sprite_icon.data.indirected_text.size = 12; + struct node *node; /* Create our window */ title = messages_get("Hotlist"); - hotlist_window_definition.title_data.indirected_text.text = title; + hotlist_window_definition.title_data.indirected_text.text = strdup(title); hotlist_window_definition.title_data.indirected_text.validation = (char *) -1; hotlist_window_definition.title_data.indirected_text.size = strlen(title); @@ -299,47 +103,70 @@ void ro_gui_hotlist_init(void) { error->errnum, error->errmess)); die(error->errmess); } + + /* Either load or create a hotlist + */ + fp = fopen("Choices:WWW.NetSurf.Hotlist", "r"); + if (!fp) { + hotlist_tree = calloc(sizeof(struct tree), 1); + if (!hotlist_tree) { + warn_user("NoMemory", 0); + return; + } + hotlist_tree->root = tree_create_folder_node(NULL, "Root"); + if (!hotlist_tree->root) { + warn_user("NoMemory", 0); + free(hotlist_tree); + hotlist_tree = NULL; + } + hotlist_tree->root->expanded = true; + node = tree_create_folder_node(hotlist_tree->root, "NetSurf"); + if (!node) + node = hotlist_tree->root; + tree_create_URL_node(node, messages_get("HotlistHomepage"), + "http://netsurf.sourceforge.net/", 0xfaf, + time(NULL), -1, 0); + tree_create_URL_node(node, messages_get("HotlistTestBuild"), + "http://netsurf.strcprstskrzkrk.co.uk/", 0xfaf, + time(NULL), -1, 0); + tree_initialise(hotlist_tree); + } else { + fclose(fp); + hotlist_tree = options_load_hotlist("Choices:WWW.NetSurf.Hotlist"); + } + if (!hotlist_tree) return; + hotlist_tree->handle = (int)hotlist_window; /* Create our toolbar */ hotlist_toolbar = ro_gui_theme_create_toolbar(NULL, THEME_HOTLIST_TOOLBAR); - ro_gui_theme_attach_toolbar(hotlist_toolbar, hotlist_window); - - /* Update the extent - */ if (hotlist_toolbar) { - extent.x1 = 16384; - extent.y1 = hotlist_toolbar->height; - extent.y0 = -16384; - error = xwimp_set_extent(hotlist_window, &extent); - if (error) { - LOG(("xwimp_set_extent: 0x%x: %s", - error->errnum, error->errmess)); - die(error->errmess); - } - reformat_pending = true; - } + ro_gui_theme_attach_toolbar(hotlist_toolbar, hotlist_window); + hotlist_tree->offset_y = hotlist_toolbar->height; + } } + /** - * Initialise a hotlist sprite for use with Tinct - * - * \param name the name of the sprite - * \param number the sprite cache number - * \return whether an error occurred + * Perform a save to the default file */ -bool ro_gui_hotlist_initialise_sprite(const char *name, int number) { - os_error *error; - sprintf(icon_name, "tr_%s", name); - error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, - (osspriteop_id)icon_name, - (osspriteop_header **)&sprite[number]); - if (error) { - warn_user("MiscError", error->errmess); - LOG(("Failed to load hotlist sprite 'tr_%s'", name)); - return true; - } - return false; +void ro_gui_hotlist_save(void) { + os_error *error; + + if (!hotlist_tree) return; + + /* Ensure we have a directory to save to later. + */ + xosfile_create_dir("<Choices$Write>.WWW", 0); + xosfile_create_dir("<Choices$Write>.WWW.NetSurf", 0); + + /* Save to our file + */ + options_save_hotlist(hotlist_tree, "<Choices$Write>.WWW.NetSurf.Hotlist"); + error = xosfile_set_type("<Choices$Write>.WWW.NetSurf.Hotlist", 0xfaf); + if (error) + LOG(("xosfile_set_type: 0x%x: %s", + error->errnum, error->errmess)); } @@ -355,7 +182,7 @@ void ro_gui_hotlist_show(void) { /* We may have failed to initialise */ - if (!hotlist_window) return; + if (!hotlist_tree) return; /* Get the window state */ @@ -370,10 +197,16 @@ void ro_gui_hotlist_show(void) { open in the centre of the screen. */ if (!(state.flags & wimp_WINDOW_OPEN)) { - /* Clear the selection/expansion states - */ - ro_gui_hotlist_update_expansion(root.child_entry, false, true, true, false, true); - ro_gui_hotlist_selection_state(root.child_entry, false, false); + + /* Cancel any editing + */ + ro_gui_tree_stop_edit(hotlist_tree); + + /* Set the default state + */ + if (hotlist_tree->root->child) + tree_handle_node_changed(hotlist_tree, hotlist_tree->root, + false, true); /* Get the current screen size */ @@ -381,11 +214,11 @@ void ro_gui_hotlist_show(void) { /* Move to the centre */ - dimension = state.visible.x1 - state.visible.x0; + dimension = 600; /*state.visible.x1 - state.visible.x0;*/ scroll_width = ro_get_vscroll_width(hotlist_window); state.visible.x0 = (screen_width - (dimension + scroll_width)) / 2; state.visible.x1 = state.visible.x0 + dimension; - dimension = state.visible.y1 - state.visible.y0; + dimension = 800; /*state.visible.y1 - state.visible.y0;*/ state.visible.y0 = (screen_height - dimension) / 2; state.visible.y1 = state.visible.y0 + dimension; state.xscroll = 0; @@ -397,2109 +230,228 @@ void ro_gui_hotlist_show(void) { */ ro_gui_menu_prepare_hotlist(); state.next = wimp_TOP; - error = xwimp_open_window((wimp_open*)&state); - if (error) { - warn_user("WimpError", error->errmess); - return; - } + ro_gui_tree_open((wimp_open*)&state, hotlist_tree); /* Set the caret position */ - xwimp_set_caret_position(state.w, -1, -100, - -100, 32, -1); -} - - -bool ro_gui_hotlist_load(void) { - fileswitch_object_type obj_type = 0; - struct hotlist_entry *netsurf; - struct hotlist_entry *entry; - - /* Check if we have an initial hotlist. OS_File does funny things relating to errors, - so we use the object type to determine success - */ - xosfile_read_stamped_no_path("Choices:WWW.NetSurf.Hotlist", &obj_type, - (bits)0, (bits)0, (int *)0, (fileswitch_attr)0, (bits)0); - if (obj_type == fileswitch_IS_FILE) { - ro_gui_hotlist_load_file("Choices:WWW.NetSurf.Hotlist"); - return true; - - } else { - /* Create a folder - */ - netsurf = ro_gui_hotlist_create_entry("NetSurf", NULL, 0, &root); - if (!netsurf) - return false; - - /* Add some content - */ - entry = ro_gui_hotlist_create_entry("NetSurf homepage", - "http://netsurf.sourceforge.net/", - 0xfaf, netsurf); - if (!entry) - return false; - entry->add_date = (time_t) -1; - entry = ro_gui_hotlist_create_entry("NetSurf test builds", - "http://netsurf.strcprstskrzkrk.co.uk/", - 0xfaf, netsurf); - if (!entry) - return false; - entry->add_date = (time_t) -1; - - /* We succeeded - */ - return true; - } -} - - -/** - * Load the hotlist from file. - * - * \param filename name of file to read - */ - -void ro_gui_hotlist_load_file(const char *filename) -{ - xmlDoc *doc; - xmlNode *html, *body, *ul; - - doc = htmlParseFile(filename, "iso-8859-1"); - if (!doc) { - warn_user("HotlistLoadError", messages_get("ParsingFail")); - return; - } - - html = ro_gui_hotlist_find_element((xmlNode *) doc, "html"); - body = ro_gui_hotlist_find_element(html, "body"); - ul = ro_gui_hotlist_find_element(body, "ul"); - if (!ul) { - xmlFreeDoc(doc); - warn_user("HotlistLoadError", - "(<html>...<body>...<ul> not found.)"); - return; - } - - ro_gui_hotlist_load_directory(ul, &root); - - xmlFreeDoc(doc); -} - - -/** - * Parse a directory represented as a ul. - * - * \param ul xmlNode for parsed ul - * \param directory directory to add this directory to - */ - -void ro_gui_hotlist_load_directory(xmlNode *ul, - struct hotlist_entry *directory) -{ - char *title; - struct hotlist_entry *dir; - xmlNode *n; - - for (n = ul->children; n; n = n->next) { - /* The ul may contain entries as a li, or directories as - * an h4 followed by a ul. Non-element nodes may be present - * (eg. text, comments), and are ignored. */ - - if (n->type != XML_ELEMENT_NODE) - continue; - - if (strcmp(n->name, "li") == 0) { - /* entry */ - ro_gui_hotlist_load_entry(n, directory); - - } else if (strcmp(n->name, "h4") == 0) { - /* directory */ - title = (char *) xmlNodeGetContent(n); - if (!title) { - warn_user("HotlistLoadError", "(Empty <h4> " - "or memory exhausted.)"); - return; - } - - for (n = n->next; - n && n->type != XML_ELEMENT_NODE; - n = n->next) - ; - if (!n || strcmp(n->name, "ul") != 0) { - /* next element isn't expected ul */ - free(title); - warn_user("HotlistLoadError", "(Expected " - "<ul> not present.)"); - return; - } - - dir = ro_gui_hotlist_create_entry(title, NULL, 0, - directory); - if (!dir) - return; - ro_gui_hotlist_load_directory(n, dir); - } - } -} - - -/** - * Parse an entry represented as a li. - * - * \param li xmlNode for parsed li - * \param directory directory to add this entry to - */ - -void ro_gui_hotlist_load_entry(xmlNode *li, - struct hotlist_entry *directory) -{ - char *url = 0; - char *title = 0; - int filetype = 0xfaf; - int add_date = -1; - int last_date = -1; - int visits = 0; - char *comment; - struct hotlist_entry *entry; - xmlNode *n; - - for (n = li->children; n; n = n->next) { - /* The li must contain an "a" element, and may contain - * some additional data as comments. */ - - if (n->type == XML_ELEMENT_NODE && - strcmp(n->name, "a") == 0) { - url = (char *) xmlGetProp(n, (const xmlChar *) "href"); - title = (char *) xmlNodeGetContent(n); - - } else if (n->type == XML_COMMENT_NODE) { - comment = (char *) xmlNodeGetContent(n); - if (!comment) - continue; - if (strncmp("Type:", comment, 5) == 0) - filetype = atoi(comment + 5); - else if (strncmp("Added:", comment, 6) == 0) - add_date = atoi(comment + 6); - else if (strncmp("LastVisit:", comment, 10) == 0) - last_date = atoi(comment + 10); - else if (strncmp("Visits:", comment, 7) == 0) - visits = atoi(comment + 7); - } - } - - if (!url || !title) { - warn_user("HotlistLoadError", "(Missing <a> in <li> or " - "memory exhausted.)"); - return; - } - - entry = ro_gui_hotlist_create_entry(title, url, filetype, directory); - if (!entry) - return; - entry->add_date = add_date; - entry->last_date = last_date; - entry->visits = visits; -} - - -/** - * Search the children of an xmlNode for an element. - * - * \param node xmlNode to search children of, or 0 - * \param name name of element to find - * \return first child of node which is an element and matches name, or - * 0 if not found or parameter node is 0 - */ - -xmlNode *ro_gui_hotlist_find_element(xmlNode *node, const char *name) -{ - xmlNode *n; - if (!node) - return 0; - for (n = node->children; - n && !(n->type == XML_ELEMENT_NODE && - strcmp(n->name, name) == 0); - n = n->next) - ; - return n; -} - - -/** - * Perform a save to the default file - */ - -void ro_gui_hotlist_save(void) { - /* Don't save if we didn't load - */ - if (!hotlist_window) return; - - /* Ensure we have a directory to save to later. - */ - xosfile_create_dir("<Choices$Write>.WWW", 0); - xosfile_create_dir("<Choices$Write>.WWW.NetSurf", 0); - - /* Save to our file - */ - ro_gui_hotlist_save_as("<Choices$Write>.WWW.NetSurf.Hotlist"); -} - - -/** - * Perform a save to a specified file - * - * /param filename the file to save to - */ - -void ro_gui_hotlist_save_as(const char *filename) -{ - int res; - xmlDoc *doc; - xmlNode *html, *head, *title, *body; - - /* Unfortunately the Browse Hotlist format is invalid HTML, - * so this is a lie. */ - doc = htmlNewDoc("http://www.w3.org/TR/html4/strict.dtd", - "-//W3C//DTD HTML 4.01//EN"); - if (!doc) { - warn_user("NoMemory", 0); - return; - } - - html = xmlNewNode(NULL, "html"); - if (!html) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return; - } - xmlDocSetRootElement(doc, html); - - head = xmlNewChild(html, NULL, "head", NULL); - if (!head) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return; - } - - title = xmlNewTextChild(head, NULL, "title", "NetSurf Hotlist"); - if (!title) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return; - } - - body = xmlNewChild(html, NULL, "body", NULL); - if (!body) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return; - } - - if (!ro_gui_hotlist_save_directory(&root, body)) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return; - } - - doc->charset = XML_CHAR_ENCODING_UTF8; - res = htmlSaveFileEnc(filename, doc, "iso-8859-1"); - if (res == -1) { - warn_user("HotlistSaveError", 0); - xmlFreeDoc(doc); - return; - } - - xmlFreeDoc(doc); - - xosfile_set_type(filename, 0xfaf); -} - - -/** - * Add a directory to the HTML tree for saving. - * - * \param directory hotlist directory to add - * \param node node to add ul to - * \return true on success, false on memory exhaustion - */ - -bool ro_gui_hotlist_save_directory(struct hotlist_entry *directory, - xmlNode *node) -{ - struct hotlist_entry *child; - xmlNode *ul, *h4; - - ul = xmlNewChild(node, NULL, "ul", NULL); - if (!ul) - return false; - - for (child = directory->child_entry; child; child = child->next_entry) { - if (child->url) { - /* entry */ - if (!ro_gui_hotlist_save_entry(child, ul)) - return false; - - } else { - /* directory */ - /* invalid HTML */ - h4 = xmlNewTextChild(ul, NULL, "h4", child->title); - if (!h4) - return false; - - if (!ro_gui_hotlist_save_directory(child, ul)) - return false; - } - } - - return true; -} - - -/** - * Add an entry to the HTML tree for saving. - * - * \param entry hotlist entry to add - * \param node node to add li to - * \return true on success, false on memory exhaustion - */ - -bool ro_gui_hotlist_save_entry(struct hotlist_entry *entry, - xmlNode *node) -{ - xmlNode *li, *a; - xmlAttr *href; - - li = xmlNewChild(node, NULL, "li", NULL); - if (!li) - return false; - - a = xmlNewTextChild(li, NULL, "a", entry->title); - if (!a) - return false; - - href = xmlNewProp(a, "href", entry->url); - if (!href) - return false; - - if (entry->filetype != 0xfaf) - if (!ro_gui_hotlist_save_entry_comment(li, - "Type", entry->filetype)) - return false; - - if (entry->add_date != -1) - if (!ro_gui_hotlist_save_entry_comment(li, - "Added", entry->add_date)) - return false; - - if (entry->last_date != -1) - if (!ro_gui_hotlist_save_entry_comment(li, - "LastVisit", entry->last_date)) - return false; - - if (entry->visits != 0) - if (!ro_gui_hotlist_save_entry_comment(li, - "Visits", entry->visits)) - return false; - - return true; -} - - -/** - * Add a special comment node to the HTML tree for saving. - * - * \param node node to add comment to - * \param name name of special comment - * \param value value of special comment - * \return true on success, false on memory exhaustion - */ - -bool ro_gui_hotlist_save_entry_comment(xmlNode *node, - const char *name, int value) -{ - char s[40]; - xmlNode *comment; - - snprintf(s, sizeof s, "%s:%i", name, value); - s[sizeof s - 1] = 0; - - comment = xmlNewComment(s); - if (!comment) - return false; - if (!xmlAddChild(node, comment)) { - xmlFreeNode(comment); - return false; - } - - return true; -} - - -/** - * Adds a hotlist entry to the root of the tree. - * - * \param title the entry title - * \param content the content to add - */ -void ro_gui_hotlist_add(char *title, struct content *content) { - ro_gui_hotlist_create_entry(title, content->url, ro_content_filetype(content), &root); -} - - -/** - * Informs the hotlist that some content has been visited - * - * \param content the content visited - */ -void hotlist_visited(struct content *content) { - if ((!content) || (!content->url)) return; - ro_gui_hotlist_visited_update(content, root.child_entry); -} - - -/** - * Informs the hotlist that some content has been visited (internal) - * - * \param content the content visited - * \param entry the entry to check siblings and children of - */ -void ro_gui_hotlist_visited_update(struct content *content, struct hotlist_entry *entry) { - char *url; - bool full = false; - - /* Update the hotlist - */ - url = content->url; - while (entry) { - if ((entry->url) && (strcmp(url, entry->url) == 0)) { - /* Check if we're going to need a full redraw downwards - */ - full = ((entry->visits == 0) || (entry->last_date == -1)); - - /* Update our values - */ - if (entry->children == -1) entry->filetype = ro_content_filetype(content); - entry->visits++; - entry->last_date = time(NULL); - ro_gui_hotlist_update_entry_size(entry); - - /* Redraw the least we can get away with - */ - if (entry->expanded) hotlist_redraw_entry(entry, full); - } - if (entry->child_entry) ro_gui_hotlist_visited_update(content, entry->child_entry); - entry = entry->next_entry; - } -} - - -/** - * Adds a hotlist entry to a folder of the tree. - * - * \param title the entry title (copied) - * \param url the entry url (NULL to create a folder) (copied) - * \param filetype filetype of entry - * \param folder the folder to add the entry into - * \return the new entry, or NULL on error and error reported - */ -struct hotlist_entry *ro_gui_hotlist_create_entry(const char *title, - const char *url, int filetype, - struct hotlist_entry *folder) { - struct hotlist_entry *entry; - url_func_result res; - - /* Check we have a title or a URL - */ - if (!title && !url) return NULL; - - /* Allocate some memory - */ - entry = (struct hotlist_entry *)calloc(1, sizeof(struct hotlist_entry)); - if (!entry) { - warn_user("NoMemory", 0); - return NULL; - } - - /* Normalise the URL and add the title if we have one, or - use the URL instead - */ - entry->url = 0; - if ((url) && ((res = url_normalize(url, &entry->url)) != URL_FUNC_OK)) { - /** \todo use correct error message */ - warn_user("NoMemory", 0); - free(entry->url); - free(entry); - return NULL; - } - if (title) { - entry->title = squash_whitespace(title); - if (!entry->title) { - warn_user("NoMemory", 0); - free(entry->url); - free(entry); - return NULL; - } - entry->title = strip(entry->title); - } else { - entry->title = strdup(entry->url); - if (!entry->title) { - warn_user("NoMemory", 0); - free(entry->url); - free(entry); - return NULL; - } - } - - /* Set the other values - */ - entry->children = (url == NULL) ? 0 : -1; - entry->filetype = filetype; - entry->visits = 0; - entry->add_date = time(NULL); - entry->last_date = (time_t)-1; - ro_gui_hotlist_update_entry_size(entry); - - /* Link into the tree - */ - ro_gui_hotlist_link_entry(folder, entry, false); - return entry; -} - - -/** - * Links a hotlist entry into the tree. - * - * \param link the entry to link as a child (folders) or before/after (link) - * \param entry the entry to link - * \param before whether to link siblings before or after the supplied link - */ -void ro_gui_hotlist_link_entry(struct hotlist_entry *link, struct hotlist_entry *entry, bool before) { - struct hotlist_entry *link_entry; - - if ((!link || !entry) || (link == entry)) return; - - /* Check if the parent is a folder or an entry - */ - if (link->children == -1) { - entry->parent_entry = link->parent_entry; - entry->parent_entry->children++; - if (before) { - entry->next_entry = link; - entry->previous_entry = link->previous_entry; - if (link->previous_entry) link->previous_entry->next_entry = entry; - link->previous_entry = entry; - if (link->parent_entry) { - if (link->parent_entry->child_entry == link) { - link->parent_entry->child_entry = entry; - } - } - } else { - entry->previous_entry = link; - entry->next_entry = link->next_entry; - if (link->next_entry) link->next_entry->previous_entry = entry; - link->next_entry = entry; - } - } else { - link_entry = link->child_entry; - - /* Link into the tree as a child at the end - */ - if (!link_entry) { - link->child_entry = entry; - entry->previous_entry = NULL; - } else { - while (link_entry->next_entry) link_entry = link_entry->next_entry; - link_entry->next_entry = entry; - entry->previous_entry = link_entry; - } - - /* Update the links - */ - entry->parent_entry = link; - entry->next_entry = NULL; - - /* Increment the number of children - */ - link->children += 1; - } - - /* Force a redraw - */ - reformat_pending = true; - xwimp_force_redraw(hotlist_window, 0, -16384, 16384, 0); -} - - -/** - * De-links a hotlist entry from the tree. - * - * \param entry the entry to de-link - */ -void ro_gui_hotlist_delink_entry(struct hotlist_entry *entry) { - if (!entry) return; - - /* Sort out if the entry was the initial child reference - */ - if (entry->parent_entry) { - entry->parent_entry->children -= 1; - if (entry->parent_entry->children == 0) entry->parent_entry->expanded = false; - if (entry->parent_entry->child_entry == entry) { - entry->parent_entry->child_entry = entry->next_entry; - } - entry->parent_entry = NULL; - } - - /* Remove the entry from siblings - */ - if (entry->previous_entry) { - entry->previous_entry->next_entry = entry->next_entry; - } - if (entry->next_entry) { - entry->next_entry->previous_entry = entry->previous_entry; - } - entry->previous_entry = NULL; - entry->next_entry = NULL; - - /* Force a redraw - */ - reformat_pending = true; - xwimp_force_redraw(hotlist_window, 0, -16384, 16384, 0); -} - - -/** - * Delete an entry and all children - * This function also performs any necessary delinking - * - * \param entry the entry to delete - * \param siblings delete all following siblings - */ -void ro_gui_hotlist_delete_entry(struct hotlist_entry *entry, bool siblings) { - struct hotlist_entry *next_entry = NULL; - while (entry) { - - /* Recurse to children first - */ - if (entry->child_entry) ro_gui_hotlist_delete_entry(entry->child_entry, true); - - /* Free our memory - */ - free(entry->url); - free(entry->title); - - /* Get the next entry before we de-link and delete - */ - if (siblings) next_entry = entry->next_entry; - - /* Delink and delete our entry and move on - */ - ro_gui_hotlist_delink_entry(entry); - free(entry); - entry = next_entry; - } -} - - -/** - * Updates and entrys size - */ -void ro_gui_hotlist_update_entry_size(struct hotlist_entry *entry) { - int width; - int max_width; - int line_number = 0; - - /* Get the width of the title - */ - xwimptextop_string_width(entry->title, - strlen(entry->title) > 256 ? 256 : strlen(entry->title), - &width); - entry->collapsed_width = width; - max_width = width; - - /* Get the width of the URL - */ - if (entry->url) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistURL"), entry->url); - if (strlen(extended_text) >= 255) { - extended_text[252] = '.'; - extended_text[253] = '.'; - extended_text[254] = '.'; - extended_text[255] = '\0'; - } - xwimptextop_string_width(extended_text, - strlen(extended_text) > 256 ? 256 : strlen(extended_text), - &width); - if (width > max_width) max_width = width; - entry->widths[line_number++] = width; - } - - /* Get the width of the add date - */ - if (entry->add_date != -1) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistAdded"), ctime(&entry->add_date)); - xwimptextop_string_width(extended_text, - strlen(extended_text) > 256 ? 256 : strlen(extended_text), - &width); - if (width > max_width) max_width = width; - entry->widths[line_number++] = width; - } - - /* Get the width of the last visit - */ - if (entry->last_date != -1) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistLast"), ctime(&entry->last_date)); - xwimptextop_string_width(extended_text, - strlen(extended_text) > 256 ? 256 : strlen(extended_text), - &width); - if (width > max_width) max_width = width; - entry->widths[line_number++] = width; - } - - /* Get the width of the visit count - */ - if (entry->visits > 0) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistVisits"), entry->visits); - xwimptextop_string_width(extended_text, - strlen(extended_text) > 256 ? 256 : strlen(extended_text), - &width); - if (width > max_width) max_width = width; - entry->widths[line_number++] = width; - } - - /* Increase the text width by the borders - */ - entry->expanded_width = max_width + HOTLIST_LEAF_INSET + HOTLIST_ICON_WIDTH + HOTLIST_TEXT_PADDING; - entry->collapsed_width += HOTLIST_LEAF_INSET + HOTLIST_ICON_WIDTH + HOTLIST_TEXT_PADDING; - reformat_pending = true; -} - - -/** - * Redraws a section of the hotlist window - * - * \param redraw the area to redraw - */ -void ro_gui_hotlist_redraw(wimp_draw *redraw) { - wimp_window_state state; - osbool more; - os_box extent = {0, 0, 0, 0};; - - /* Reset our min/max sizes - */ - max_width = 0; - max_height = 0; - - /* Redraw each rectangle - */ - more = wimp_redraw_window(redraw); - while (more) { - clip_x0 = redraw->clip.x0; - clip_y0 = redraw->clip.y0; - clip_x1 = redraw->clip.x1; - clip_y1 = redraw->clip.y1; - origin_x = redraw->box.x0 - redraw->xscroll; - origin_y = redraw->box.y1 - redraw->yscroll; - ro_gui_hotlist_redraw_tree(root.child_entry, 0, - origin_x + 8, origin_y - 4); - more = wimp_get_rectangle(redraw); - } - - /* Check if we should reformat - */ - if (reformat_pending) { - max_width += 8; - max_height -= 4; - if (max_width < 600) max_width = 600; - if (max_height > -800) max_height = -800; - extent.x1 = max_width; - extent.y0 = max_height; - if (hotlist_toolbar) { - extent.y1 += hotlist_toolbar->height; - } - xwimp_set_extent(hotlist_window, &extent); - state.w = hotlist_window; - wimp_get_window_state(&state); - wimp_open_window((wimp_open *) &state); - reformat_pending = false; - } -} - - -/** - * Redraws a section of the hotlist window (non-WIMP interface) - * - * \param entry the entry to draw descendants and siblings of - * \param level the tree level of the entry - * \param x0 the x co-ordinate to plot from - * \param y0 the y co-ordinate to plot from - * \returns the height of the tree - */ -int ro_gui_hotlist_redraw_tree(struct hotlist_entry *entry, int level, int x0, int y0) { - bool first = true; - int cumulative = 0; - int height = 0; - int box_y0; - - if (!entry) return 0; - - /* Repeatedly draw our entries - */ - while (entry) { - - /* Redraw the item - */ - height = ro_gui_hotlist_redraw_item(entry, level, x0 + HOTLIST_LEAF_INSET, y0); - box_y0 = y0; - cumulative += height; - - /* Update the entry position - */ - if (entry->children == -1) { - entry->height = height; - } else { - entry->height = HOTLIST_LINE_HEIGHT; - } - entry->x0 = x0 - origin_x; - entry->y0 = y0 - origin_y - entry->height; - if (entry->expanded) { - entry->width = entry->expanded_width; - } else { - entry->width = entry->collapsed_width; - } - - /* Get the maximum extents - */ - if ((x0 + entry->width) > (max_width + origin_x)) - max_width = x0 + entry->width - origin_x; - if ((y0 - height) < (max_height + origin_y)) - max_height = y0 - height - origin_y; - - /* Draw the vertical links - */ - if (entry->next_entry) { - /* Draw a half-line for the first entry in the top tree - */ - if (first && (level == 0)) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_BLINE], x0 + 8, y0 - HOTLIST_LINE_HEIGHT, - tinct_BILINEAR_FILTER); - y0 -= HOTLIST_LINE_HEIGHT; - height -= HOTLIST_LINE_HEIGHT; - } - - /* Draw the rest of the lines - */ - while (height > 0) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_LINE], x0 + 8, y0 - HOTLIST_LINE_HEIGHT, - tinct_BILINEAR_FILTER); - y0 -= HOTLIST_LINE_HEIGHT; - height -= HOTLIST_LINE_HEIGHT; - } - - } else { - /* Draw a half-line for the last entry - */ - if (!first || (level != 0)) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_TLINE], x0 + 8, y0 - 22, - tinct_BILINEAR_FILTER); - height -= HOTLIST_LINE_HEIGHT; - y0 -= HOTLIST_LINE_HEIGHT; - } - } - - /* Draw the expansion type - */ - if (entry->children == 0) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_ENTRY], x0, box_y0 - 23, - tinct_BILINEAR_FILTER); - } else { - if (entry->expanded) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_COLLAPSE], x0, box_y0 - 31, - tinct_BILINEAR_FILTER); - } else { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_EXPAND], x0, box_y0 - 31, - tinct_BILINEAR_FILTER); - } - } - - /* Move to the next entry - */ - entry = entry->next_entry; - first = false; - } - - /* Return our height - */ - return cumulative; -} - - -/** - * Redraws an entry in the tree and any children - * - * \param entry the entry to redraw - * \param level the level of the entry - * \param x0 the x co-ordinate to plot at - * \param y0 the y co-ordinate to plot at - * \return the height of the entry - */ -int ro_gui_hotlist_redraw_item(struct hotlist_entry *entry, int level, int x0, int y0) { - int height = HOTLIST_LINE_HEIGHT; - int line_y0; - int line_height; - - /* Set the correct height - */ - if ((entry->children == -1) && (entry->expanded)) { - if (entry->url) height += HOTLIST_LINE_HEIGHT; - if (entry->visits > 0) height += HOTLIST_LINE_HEIGHT; - if (entry->add_date != -1) height += HOTLIST_LINE_HEIGHT; - if (entry->last_date != -1) height += HOTLIST_LINE_HEIGHT; - } - - /* Check whether we need to redraw - */ - if ((x0 < clip_x1) && (y0 > clip_y0) && ((x0 + entry->width) > clip_x0) && - ((y0 - height) < clip_y1)) { - - - /* Update the selection state - */ - text_icon.flags = wimp_ICON_TEXT | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | - (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT) | - wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED; - if (entry->selected) { - sprite_icon.flags |= wimp_ICON_SELECTED; - text_icon.flags |= wimp_ICON_SELECTED; - text_icon.flags |= wimp_ICON_FILLED; - } - - /* Draw our icon type - */ - sprite_icon.extent.x0 = x0 - origin_x; - sprite_icon.extent.x1 = x0 - origin_x + HOTLIST_ICON_WIDTH; - sprite_icon.extent.y0 = y0 - origin_y - HOTLIST_LINE_HEIGHT; - sprite_icon.extent.y1 = y0 - origin_y; - sprite_icon.data.indirected_sprite.id = (osspriteop_id)icon_name; - if (entry->children != -1) { - if ((entry->expanded) && (entry->children > 0)) { - sprintf(icon_name, "small_diro"); - hotlist_ensure_sprite(icon_name, "small_dir"); - } else { - sprintf(icon_name, "small_dir"); - } - } else { - /* Get the icon sprite - */ - sprintf(icon_name, "small_%x", entry->filetype); - hotlist_ensure_sprite(icon_name, "small_xxx"); - } - xwimp_plot_icon(&sprite_icon); - - /* Draw our textual information - */ - text_icon.data.indirected_text.text = entry->title; - text_icon.extent.x0 = x0 - origin_x + HOTLIST_ICON_WIDTH; - text_icon.extent.x1 = x0 - origin_x + entry->collapsed_width - HOTLIST_LEAF_INSET; - text_icon.extent.y0 = y0 - origin_y - HOTLIST_LINE_HEIGHT + 2; - text_icon.extent.y1 = y0 - origin_y - 2; - xwimp_plot_icon(&text_icon); - - /* Clear the selection state - */ - if (entry->selected) { - sprite_icon.flags &= ~wimp_ICON_SELECTED; - } - - /* Draw our further information if expanded - */ - if ((entry->children == -1) && (entry->expanded) && (height > HOTLIST_LINE_HEIGHT)) { - text_icon.flags = wimp_ICON_TEXT | (wimp_COLOUR_DARK_GREY << wimp_ICON_FG_COLOUR_SHIFT) | - wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED; - text_icon.extent.y0 = y0 - origin_y - HOTLIST_LINE_HEIGHT; - text_icon.extent.y1 = y0 - origin_y; - - /* Draw the lines - */ - y0 -= HOTLIST_LINE_HEIGHT; - line_y0 = y0; - line_height = height - HOTLIST_LINE_HEIGHT; - while (line_height > 0) { - if (line_height == HOTLIST_LINE_HEIGHT) { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_TLINE], x0 + 16, line_y0 - 22, - tinct_BILINEAR_FILTER); - } else { - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_LINE], x0 + 16, line_y0 - HOTLIST_LINE_HEIGHT, - tinct_BILINEAR_FILTER); - } - _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), - sprite[HOTLIST_ENTRY], x0 + 8, line_y0 - 23, - tinct_BILINEAR_FILTER); - line_height -= HOTLIST_LINE_HEIGHT; - line_y0 -= HOTLIST_LINE_HEIGHT; - } - - /* Set the right extent of the icon to be big enough for anything - */ - text_icon.extent.x1 = x0 - origin_x + 4096; - - /* Plot the URL text - */ - text_icon.data.indirected_text.text = extended_text; - if (entry->url) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistURL"), entry->url); - if (strlen(extended_text) >= 255) { - extended_text[252] = '.'; - extended_text[253] = '.'; - extended_text[254] = '.'; - extended_text[255] = '\0'; - } - text_icon.extent.y0 -= HOTLIST_LINE_HEIGHT; - text_icon.extent.y1 -= HOTLIST_LINE_HEIGHT; - xwimp_plot_icon(&text_icon); - } - - /* Plot the date added text - */ - if (entry->add_date != -1) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistAdded"), ctime(&entry->add_date)); - text_icon.extent.y0 -= HOTLIST_LINE_HEIGHT; - text_icon.extent.y1 -= HOTLIST_LINE_HEIGHT; - xwimp_plot_icon(&text_icon); - } - - /* Plot the last visited text - */ - if (entry->last_date != -1) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistLast"), ctime(&entry->last_date)); - text_icon.extent.y0 -= HOTLIST_LINE_HEIGHT; - text_icon.extent.y1 -= HOTLIST_LINE_HEIGHT; - xwimp_plot_icon(&text_icon); - } - - /* Plot the visit count text - */ - if (entry->visits > 0) { - snprintf(extended_text, HOTLIST_TEXT_BUFFER, - messages_get("HotlistVisits"), entry->visits); - text_icon.extent.y0 -= HOTLIST_LINE_HEIGHT; - text_icon.extent.y1 -= HOTLIST_LINE_HEIGHT; - xwimp_plot_icon(&text_icon); - } - } - } - - /* Draw any children - */ - if ((entry->child_entry) && (entry->expanded)) { - height += ro_gui_hotlist_redraw_tree(entry->child_entry, level + 1, - x0 + 8, y0 - HOTLIST_LINE_HEIGHT); - } - return height; + xwimp_set_caret_position(state.w, -1, -100, -100, 32, -1); } /** * Respond to a mouse click * - * /param pointer the pointer state + * \param pointer the pointer state */ void ro_gui_hotlist_click(wimp_pointer *pointer) { - wimp_caret caret; - wimp_drag drag; - struct hotlist_entry *entry; - wimp_window_state state; - wimp_mouse_state buttons; - int x, y; - int x_offset; - int y_offset; - bool no_entry = false; - os_error *error; - os_box box = { pointer->pos.x - 34, pointer->pos.y - 34, - pointer->pos.x + 34, pointer->pos.y + 34 }; - int selection; - - /* Get the button state - */ - buttons = pointer->buttons; - - /* Get the window state. Quite why the Wimp can't give relative - positions is beyond me. - */ - state.w = hotlist_window; - wimp_get_window_state(&state); - - /* Translate by the origin/scroll values - */ - x = (pointer->pos.x - (state.visible.x0 - state.xscroll)); - y = (pointer->pos.y - (state.visible.y1 - state.yscroll)); - - /* We want the caret on a click - */ - error = xwimp_get_caret_position(&caret); - if (error) { - LOG(("xwimp_get_caret_position: 0x%x: %s", error->errnum, - error->errmess)); - } - if (((pointer->buttons == (wimp_CLICK_SELECT << 8)) || - (pointer->buttons == (wimp_CLICK_ADJUST << 8))) && - (caret.w != state.w)) { - error = xwimp_set_caret_position(state.w, -1, -100, - -100, 32, -1); - if (error) { - LOG(("xwimp_set_caret_position: 0x%x: %s", - error->errnum, error->errmess)); - } - } - - /* Find our entry - */ - entry = ro_gui_hotlist_find_entry(x, y, root.child_entry); - if (entry) { - /* Check if we clicked on the expanding bit - */ - x_offset = x - entry->x0; - y_offset = y - (entry->y0 + entry->height); - if (((x_offset < HOTLIST_LEAF_INSET) && (y_offset > -HOTLIST_LINE_HEIGHT) && - ((buttons == wimp_CLICK_SELECT << 8) || (buttons == wimp_CLICK_ADJUST << 8) || - (buttons == wimp_DOUBLE_SELECT) || (buttons == wimp_DOUBLE_ADJUST))) || - ((entry->children != -1) && - ((buttons == wimp_DOUBLE_SELECT) || (buttons == wimp_DOUBLE_ADJUST)))) { - if (entry->children != 0) { - ro_gui_hotlist_update_expansion(entry->child_entry, false, true, true, false, true); - ro_gui_hotlist_selection_state(entry->child_entry, - false, false); - entry->expanded = !entry->expanded; - if (x_offset >= HOTLIST_LEAF_INSET) entry->selected = false; - reformat_pending = true; - hotlist_redraw_entry(entry, true); - ro_gui_menu_prepare_hotlist(); - } - } else if (x_offset >= HOTLIST_LEAF_INSET) { - - /* We treat a menu click as a Select click if we have no selections - */ - if (buttons == wimp_CLICK_MENU) { - if (ro_gui_hotlist_selection_count(root.child_entry, true) == 0) { - menu_selection = true; - buttons = (wimp_CLICK_SELECT << 8); - } - } - - /* Check for selection - */ - if (buttons == (wimp_CLICK_SELECT << 8)) { - if (!entry->selected) { - ro_gui_hotlist_selection_state(root.child_entry, - false, true); - entry->selected = true; - hotlist_redraw_entry_title(entry); - ro_gui_menu_prepare_hotlist(); - - } - } else if (buttons == (wimp_CLICK_ADJUST << 8)) { - entry->selected = !entry->selected; - hotlist_redraw_entry_title(entry); - ro_gui_menu_prepare_hotlist(); - } - - /* Check if we should open the URL - */ - if (((buttons == wimp_DOUBLE_SELECT) || (buttons == wimp_DOUBLE_ADJUST)) && - (entry->children == -1)) { - browser_window_create(entry->url, NULL, 0); - if (buttons == wimp_DOUBLE_SELECT) { - ro_gui_hotlist_selection_state(root.child_entry, - false, true); - ro_gui_menu_prepare_hotlist(); - } else { - entry->selected = false; - ro_gui_dialog_close_persistant(hotlist_window); - xwimp_close_window(hotlist_window); - } - } - - /* Check if we should start a drag - */ - if ((buttons == (wimp_CLICK_SELECT <<4)) || (buttons == (wimp_CLICK_ADJUST << 4))) { - selection = ro_gui_hotlist_get_selected(true); - if (selection > 0) { - gui_current_drag_type = GUI_DRAG_HOTLIST_MOVE; - if (selection > 1) { - sprintf(drag_name, "package"); - } else { - if (entry->children != -1) { - if ((entry->expanded) && (entry->children > 0)) { - sprintf(drag_name, "directoryo"); - hotlist_ensure_sprite(drag_name, "directory"); - } else { - sprintf(drag_name, "directory"); - } - } else { - sprintf(drag_name, "file_%x", entry->filetype); - hotlist_ensure_sprite(drag_name, "file_xxx"); - } - } - error = xdragasprite_start(dragasprite_HPOS_CENTRE | - dragasprite_VPOS_CENTRE | - dragasprite_BOUND_POINTER | - dragasprite_DROP_SHADOW, - (osspriteop_area *) 1, drag_name, &box, 0); - dragging = true; - } - } - } else { - if (!((x_offset < HOTLIST_LEAF_INSET) && (y_offset > -HOTLIST_LINE_HEIGHT))) { - no_entry = true; - } - } - } else { - no_entry = true; - } - - /* Get the original button state back - */ - buttons = pointer->buttons; - - /* Create a menu if we should - */ - if (buttons == wimp_CLICK_MENU) { + ro_gui_tree_click(pointer, hotlist_tree); + if (pointer->buttons == wimp_CLICK_MENU) ro_gui_create_menu(hotlist_menu, pointer->pos.x, pointer->pos.y, NULL); - menu_open = true; - return; - } - - /* Handle a click without an entry - */ - if (no_entry) { - /* Deselect everything if we click nowhere - */ - if (buttons == (wimp_CLICK_SELECT << 8)) { - ro_gui_hotlist_selection_state(root.child_entry, - false, true); - ro_gui_menu_prepare_hotlist(); - } - - /* Handle the start of a drag - */ - if (buttons == (wimp_CLICK_SELECT << 4) || - buttons == (wimp_CLICK_ADJUST << 4)) { - - /* Clear the current selection - */ - if (buttons == (wimp_CLICK_SELECT << 4)) { - ro_gui_hotlist_selection_state(root.child_entry, - false, true); - ro_gui_menu_prepare_hotlist(); - } - - /* Start a drag box - */ - drag_buttons = buttons; - gui_current_drag_type = GUI_DRAG_HOTLIST_SELECT; - drag.w = hotlist_window; - drag.type = wimp_DRAG_USER_RUBBER; - drag.initial.x0 = pointer->pos.x; - drag.initial.x1 = pointer->pos.x; - drag.initial.y0 = pointer->pos.y; - drag.initial.y1 = pointer->pos.y; - drag.bbox.x0 = state.visible.x0; - drag.bbox.x1 = state.visible.x1; - drag.bbox.y0 = state.visible.y0; - drag.bbox.y1 = state.visible.y1; - if (hotlist_toolbar) drag.bbox.y1 -= hotlist_toolbar->height; - xwimp_drag_box(&drag); - dragging = true; - } - } -} - - -/** - * Find an entry at a particular position - * - * \param x the x co-ordinate - * \param y the y co-ordinate - * \param entry the entry to check down from (root->child_entry for the entire tree) - * /return the entry occupying the positon - */ -struct hotlist_entry *ro_gui_hotlist_find_entry(int x, int y, struct hotlist_entry *entry) { - struct hotlist_entry *find_entry; - int inset_x = 0; - int inset_y = 0; - - /* Check we have an entry (only applies if we have an empty hotlist) - */ - if (!entry) return NULL; - - /* Get the first child entry - */ - while (entry) { - /* Check if this entry could possibly match - */ - if ((x > entry->x0) && (y > entry->y0) && (x < (entry->x0 + entry->width)) && - (y < (entry->y0 + entry->height))) { - - /* The top line extends all the way left - */ - if (y - (entry->y0 + entry->height) > -HOTLIST_LINE_HEIGHT) { - if (x < (entry->x0 + entry->collapsed_width)) return entry; - return NULL; - } - - /* No other entry can occupy the left edge - */ - inset_x = x - entry->x0 - HOTLIST_LEAF_INSET - HOTLIST_ICON_WIDTH; - if (inset_x < 0) return NULL; - - /* Check the right edge against our various widths - */ - inset_y = -((y - entry->y0 - entry->height) / HOTLIST_LINE_HEIGHT); - if (inset_x < (entry->widths[inset_y - 1] + HOTLIST_TEXT_PADDING)) return entry; - return NULL; - } - - /* Continue onwards - */ - if ((entry->child_entry) && (entry->expanded)) { - find_entry = ro_gui_hotlist_find_entry(x, y, entry->child_entry); - if (find_entry) return find_entry; - } - entry = entry->next_entry; - } - return NULL; -} - - -/** - * Updated the selection state of the tree - * - * \param entry the entry to update all siblings and descendants of - * \param selected the selection state to set - * \param redraw update the icons in the Wimp - * \return the number of entries that have changed - */ -int ro_gui_hotlist_selection_state(struct hotlist_entry *entry, bool selected, bool redraw) { - int changes = 0; - - /* Check we have an entry (only applies if we have an empty hotlist) - */ - if (!entry) return 0; - - /* Get the first child entry - */ - while (entry) { - /* Check this entry - */ - if (entry->selected != selected) { - /* Update the selection state - */ - entry->selected = selected; - changes++; - - /* Redraw the entrys first line - */ - if (redraw) hotlist_redraw_entry_title(entry); - } - - /* Continue onwards - */ - if ((entry->child_entry) && ((!selected) || (entry->expanded))) { - changes += ro_gui_hotlist_selection_state(entry->child_entry, - selected, redraw & (entry->expanded)); - } - entry = entry->next_entry; - } - return changes; -} - - -/** - * Returns the first selected item - * - * \param entry the search siblings and children of - * \return the first selected item - */ -struct hotlist_entry *ro_gui_hotlist_first_selection(struct hotlist_entry *entry) { - struct hotlist_entry *test_entry; - - /* Check we have an entry (only applies if we have an empty hotlist) - */ - if (!entry) return NULL; - - /* Work through our entries - */ - while (entry) { - if (entry->selected) return entry; - if (entry->child_entry) { - test_entry = ro_gui_hotlist_first_selection(entry->child_entry); - if (test_entry) return test_entry; - } - entry = entry->next_entry; - } - return NULL; -} - - -/** - * Return the current number of selected items (internal interface) - * - * \param entry the entry to count siblings and children of - */ -int ro_gui_hotlist_selection_count(struct hotlist_entry *entry, bool folders) { - int count = 0; - if (!entry) return 0; - while (entry) { - if ((entry->selected) && (folders || (entry->children == -1))) count++; - if (entry->child_entry) count += ro_gui_hotlist_selection_count(entry->child_entry, folders); - entry = entry->next_entry; - } - return count; -} - - -/** - * Launch the current selection (internal interface) - * - * \param entry the entry to launch siblings and children of - */ -void ro_gui_hotlist_launch_selection(struct hotlist_entry *entry) { - if (!entry) return; - while (entry) { - if ((entry->selected) && (entry->url)) browser_window_create(entry->url, NULL, 0); - if (entry->child_entry) ro_gui_hotlist_launch_selection(entry->child_entry); - entry = entry->next_entry; - } -} - - -/** - * Invalidate the statistics for any selected items (internal interface) - * - * \param entry the entry to update siblings and children of - */ -void ro_gui_hotlist_invalidate_statistics(struct hotlist_entry *entry) { - if (!entry) return; - while (entry) { - if ((entry->selected) && (entry->children == -1)) { - entry->visits = 0; - entry->last_date = (time_t)-1; - if (entry->expanded) hotlist_redraw_entry(entry, true); - } - if (entry->child_entry) ro_gui_hotlist_invalidate_statistics(entry->child_entry); - entry = entry->next_entry; - } -} - - -/** - * Set the process flag for the current selection (internal interface) - * - * \param entry the entry to modify siblings and children of - */ -void ro_gui_hotlist_selection_to_process(struct hotlist_entry *entry) { - if (!entry) return; - while (entry) { - entry->process = entry->selected; - if (entry->child_entry) ro_gui_hotlist_selection_to_process(entry->child_entry); - entry = entry->next_entry; - } -} - - -/** - * Toggles the expanded state for selected icons - * If neither expand not contract are set then the entries are toggled - * - * \param entry the entry to update all siblings and descendants of - * \param only_selected whether to only update selected icons - * \param folders whether to update folders - * \param links whether to update links - * \param expand force all entries to be expanded (dominant) - * \param contract force all entries to be contracted (recessive) - */ -void ro_gui_hotlist_update_expansion(struct hotlist_entry *entry, bool only_selected, - bool folders, bool links, bool expand, bool contract) { - bool current; - - /* Set a reformat to be pending - */ - reformat_pending = true; - - /* Check we have an entry (only applies if we have an empty hotlist) - */ - if (!entry) return; - - /* Get the first child entry - */ - while (entry) { - /* Check this entry - */ - if ((entry->selected) || (!only_selected)) { - current = entry->expanded; - - /* Only update what we should - */ - if (((links) && (entry->children == -1)) || ((folders) && (entry->children > 0))) { - /* Update the expansion state - */ - if (expand) { - entry->expanded = true; - } else if (contract) { - entry->expanded = false; - } else { - entry->expanded = !entry->expanded; - } - } - - /* If we have contracted then we de-select and collapse any children - */ - if (entry->child_entry && !entry->expanded) { - ro_gui_hotlist_update_expansion(entry->child_entry, false, true, true, false, true); - ro_gui_hotlist_selection_state(entry->child_entry, false, false); - } - - /* Redraw the entrys first line - */ - if (current != entry->expanded) hotlist_redraw_entry(entry, true); - } - - /* Continue onwards (child entries cannot be selected if the parent is - not expanded) - */ - if (entry->child_entry && entry->expanded) { - ro_gui_hotlist_update_expansion(entry->child_entry, - only_selected, folders, links, expand, contract); - } - entry = entry->next_entry; - } + else + ro_gui_menu_prepare_hotlist(); } /** - * Updated the selection state of the tree + * Respond to a keypress * - * \param entry the entry to update all siblings and descendants of - * \param x0 the left edge of the box - * \param y0 the top edge of the box - * \param x1 the right edge of the box - * \param y1 the bottom edge of the box - * \param toggle toggle the selection state, otherwise set - * \param redraw update the icons in the Wimp + * \param key the key pressed */ -void ro_gui_hotlist_selection_drag(struct hotlist_entry *entry, - int x0, int y0, int x1, int y1, - bool toggle, bool redraw) { - bool do_update; - int line; - int test_y; - - /* Check we have an entry (only applies if we have an empty hotlist) - */ - if (!entry) return; - - /* Get the first child entry - */ - while (entry) { - /* Check if this entry could possibly match - */ - if ((x1 > (entry->x0 + HOTLIST_LEAF_INSET)) && (y0 > entry->y0) && - (x0 < (entry->x0 + entry->width)) && - (y1 < (entry->y0 + entry->height))) { - do_update = false; - - /* Check the exact area of the title line - */ - if ((x1 > (entry->x0 + HOTLIST_LEAF_INSET)) && - (y0 > entry->y0 + entry->height - HOTLIST_LINE_HEIGHT) && - (x0 < (entry->x0 + entry->collapsed_width)) && - (y1 < (entry->y0 + entry->height))) { - do_update = true; - } - - /* Check the other lines - */ - line = 1; - test_y = entry->y0 + entry->height - HOTLIST_LINE_HEIGHT; - while (((line * HOTLIST_LINE_HEIGHT) < entry->height) && (!do_update)) { - /* Check this line - */ - if ((x1 > (entry->x0 + HOTLIST_LEAF_INSET + HOTLIST_ICON_WIDTH)) && - (y1 < test_y) && (y0 > test_y - HOTLIST_LINE_HEIGHT) && - (x0 < (entry->x0 + entry->widths[line - 1] + - HOTLIST_LEAF_INSET + HOTLIST_ICON_WIDTH + - HOTLIST_TEXT_PADDING))) { - do_update = true; - } - - /* Move to the next line - */ - line++; - test_y -= HOTLIST_LINE_HEIGHT; - } - - /* Redraw the entrys first line - */ - if (do_update) { - if (toggle) { - entry->selected = !entry->selected; - } else { - entry->selected = true; - } - if (redraw) hotlist_redraw_entry_title(entry); - } - } - - /* Continue onwards - */ - if ((entry->child_entry) && (entry->expanded)) { - ro_gui_hotlist_selection_drag(entry->child_entry, - x0, y0, x1, y1, toggle, redraw); - } - entry = entry->next_entry; - do_update = false; - } -} - - -/** - * The end of a selection drag has been reached - * - * \param drag the final drag co-ordinates - */ -void ro_gui_hotlist_selection_drag_end(wimp_dragged *drag) { - wimp_window_state state; - int x0, y0, x1, y1; - int toolbar_height = 0; - - /* Reset our dragging state - */ - dragging = false; - - /* Get the toolbar height - */ - if (hotlist_toolbar) toolbar_height = hotlist_toolbar->height * 2; - - /* Get the window state to make everything relative - */ - state.w = hotlist_window; - wimp_get_window_state(&state); - - /* Create the relative positions - */ - x0 = drag->final.x0 - state.visible.x0 - state.xscroll; - x1 = drag->final.x1 - state.visible.x0 - state.xscroll; - y0 = drag->final.y0 - state.visible.y1 - state.yscroll + toolbar_height; - y1 = drag->final.y1 - state.visible.y1 - state.yscroll + toolbar_height; - - /* Make sure x0 < x1 and y0 > y1 - */ - if (x0 > x1) { - x0 ^= x1; - x1 ^= x0; - x0 ^= x1; - } - if (y0 < y1) { - y0 ^= y1; - y1 ^= y0; - y0 ^= y1; - } - - /* Update the selection state - */ - if (drag_buttons == (wimp_CLICK_SELECT << 4)) { - ro_gui_hotlist_selection_drag(root.child_entry, x0, y0, x1, y1, false, true); - } else { - ro_gui_hotlist_selection_drag(root.child_entry, x0, y0, x1, y1, true, true); - } +bool ro_gui_hotlist_keypress(int key) { + // struct node_element *edit = hotlist_tree->editing; + bool result = ro_gui_tree_keypress(key, hotlist_tree); ro_gui_menu_prepare_hotlist(); -} - - -/** - * The end of a item moving drag has been reached - * - * \param drag the final drag co-ordinates - */ -void ro_gui_hotlist_move_drag_end(wimp_dragged *drag) { - wimp_pointer pointer; - int toolbar_height = 0; - wimp_window_state state; - struct hotlist_entry *test_entry; - struct hotlist_entry *entry; - int x, y, x0, y0, x1, y1; - bool before = false; - - /* Reset our dragging state - */ - dragging = false; - - /* Check we dropped to our window - */ - xwimp_get_pointer_info(&pointer); - if (pointer.w != hotlist_window) return; - - /* Get the toolbar height - */ - if (hotlist_toolbar) toolbar_height = hotlist_toolbar->height * 2; - - /* Set the process flag for all selected items - */ - ro_gui_hotlist_selection_to_process(root.child_entry); - - /* Get the window state to make everything relative - */ - state.w = hotlist_window; - wimp_get_window_state(&state); - - /* Create the relative positions - */ - x0 = drag->final.x0 - state.visible.x0 - state.xscroll; - x1 = drag->final.x1 - state.visible.x0 - state.xscroll; - y0 = drag->final.y0 - state.visible.y1 - state.yscroll + toolbar_height; - y1 = drag->final.y1 - state.visible.y1 - state.yscroll + toolbar_height; - x = (x0 + x1) / 2; - y = (y0 + y1) / 2; - - /* Find our entry - */ - entry = ro_gui_hotlist_find_entry(x, y, root.child_entry); - if (!entry) entry = &root; - - /* No parent of the destination can be processed - */ - test_entry = entry; - while (test_entry != NULL) { - if (test_entry->process) return; - test_entry = test_entry->parent_entry; - } - /* Check for before/after - */ - before = ((y - (entry->y0 + entry->height)) > (-HOTLIST_LINE_HEIGHT / 2)); - - /* Start our recursive moving - */ - while (ro_gui_hotlist_move_processing(root.child_entry, entry, before)); -} - - - -bool ro_gui_hotlist_move_processing(struct hotlist_entry *entry, struct hotlist_entry *destination, bool before) { - bool result = false; - if (!entry) return false; - while (entry) { - if (entry->process) { - entry->process = false; - ro_gui_hotlist_delink_entry(entry); - ro_gui_hotlist_link_entry(destination, entry, before); - result = true; - } - if (entry->child_entry) { - result |= ro_gui_hotlist_move_processing(entry->child_entry, destination, before); - } - entry = entry->next_entry; - } +/* if ((edit) && (!hotlist_tree->editing)) + ro_gui_hotlist_save(); +*/ return result; } + /** - * Handle a menu being closed + * Handles a menu closed event */ void ro_gui_hotlist_menu_closed(void) { - menu_open = false; - if (menu_selection) { - ro_gui_hotlist_selection_state(root.child_entry, false, true); - menu_selection = false; - } + ro_gui_tree_menu_closed(hotlist_tree); + current_menu = NULL; + ro_gui_menu_prepare_hotlist(); } + /** - * Handle a keypress + * Respond to a mouse click * - * \param key the key pressed - * \return whether the key was processed + * \param pointer the pointer state */ -bool ro_gui_hotlist_keypress(int key) { - wimp_window_state state; - int y; - - /* Handle basic keys - */ - switch (key) { - case 1: /* CTRL+A */ - ro_gui_hotlist_selection_state(root.child_entry, true, true); - if (menu_open) ro_gui_create_menu(hotlist_menu, 0, 0, NULL); - return true; - case 26: /* CTRL+Z */ - ro_gui_hotlist_selection_state(root.child_entry, false, true); - if (menu_open) ro_gui_create_menu(hotlist_menu, 0, 0, NULL); - return true; - case 32: /* SPACE */ - ro_gui_hotlist_update_expansion(root.child_entry, true, true, true, false, false); - if (menu_open) ro_gui_create_menu(hotlist_menu, 0, 0, NULL); - return true; - case wimp_KEY_RETURN: - ro_gui_hotlist_launch_selection(root.child_entry); - return true; - case wimp_KEY_F3: - ro_gui_hotlist_save(); - return true; - case wimp_KEY_UP: - case wimp_KEY_DOWN: - case wimp_KEY_PAGE_UP: - case wimp_KEY_PAGE_DOWN: - case wimp_KEY_CONTROL | wimp_KEY_UP: - case wimp_KEY_CONTROL | wimp_KEY_DOWN: - break; - - default: - return false; - } - - /* Handle keypress scrolling - */ - state.w = hotlist_window; - wimp_get_window_state(&state); - y = state.visible.y1 - state.visible.y0 - 32; - switch (key) { - case wimp_KEY_UP: - state.yscroll += 32; - break; - case wimp_KEY_DOWN: - state.yscroll -= 32; - break; - case wimp_KEY_PAGE_UP: - state.yscroll += y; - break; - case wimp_KEY_PAGE_DOWN: - state.yscroll -= y; - break; - case wimp_KEY_CONTROL | wimp_KEY_UP: - state.yscroll = 1000; - break; - case wimp_KEY_CONTROL | wimp_KEY_DOWN: - state.yscroll = -0x10000000; - break; - } - xwimp_open_window((wimp_open *) &state); - return true; -} - - void ro_gui_hotlist_toolbar_click(wimp_pointer* pointer) { - int selection; + struct node *node; - /* Store the toolbar - */ current_toolbar = hotlist_toolbar; - - /* Handle Menu clicks - */ - if (pointer->buttons == wimp_CLICK_MENU) { - ro_gui_create_menu(toolbar_menu, pointer->pos.x, - pointer->pos.y, NULL); - return; - } + ro_gui_tree_stop_edit(hotlist_tree); - /* Handle the buttons appropriately - */ switch (pointer->i) { case ICON_TOOLBAR_CREATE: - hotlist_insert = false; - if (pointer->buttons == wimp_CLICK_SELECT) { - ro_gui_hotlist_prepare_folder_dialog(false); - ro_gui_dialog_open_persistant(hotlist_window, dialog_folder, true); - } else { - ro_gui_hotlist_prepare_entry_dialog(false); - ro_gui_dialog_open_persistant(hotlist_window, dialog_entry, true); - } + node = tree_create_folder_node(hotlist_tree->root, + messages_get("TreeNewFolder")); + tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, + 0, NODE_INSTEP, 16384); + tree_handle_node_changed(hotlist_tree, node, false, true); + ro_gui_tree_start_edit(hotlist_tree, &node->data, NULL); break; case ICON_TOOLBAR_OPEN: - selection = ro_gui_hotlist_get_selected(true); - ro_gui_hotlist_update_expansion(root.child_entry, (selection != 0), true, false, + tree_handle_expansion(hotlist_tree, hotlist_tree->root, (pointer->buttons == wimp_CLICK_SELECT), - (pointer->buttons == wimp_CLICK_ADJUST)); + true, false); break; case ICON_TOOLBAR_EXPAND: - selection = ro_gui_hotlist_get_selected(true); - ro_gui_hotlist_update_expansion(root.child_entry, (selection != 0), false, true, + tree_handle_expansion(hotlist_tree, hotlist_tree->root, (pointer->buttons == wimp_CLICK_SELECT), - (pointer->buttons == wimp_CLICK_ADJUST)); + false, true); break; case ICON_TOOLBAR_DELETE: - ro_gui_hotlist_delete_selected(); + tree_delete_selected_nodes(hotlist_tree, + hotlist_tree->root); break; case ICON_TOOLBAR_LAUNCH: - ro_gui_hotlist_keypress(wimp_KEY_RETURN); + ro_gui_tree_launch_selected(hotlist_tree); break; } } -void ro_gui_hotlist_prepare_folder_dialog(bool selected) { - struct hotlist_entry *entry = NULL; - if (selected) entry = ro_gui_hotlist_first_selection(root.child_entry); - - /* Update the title - */ - dialog_folder_add = selected; - if (selected) { - ro_gui_set_window_title(dialog_folder, messages_get("EditFolder")); - } else { - ro_gui_set_window_title(dialog_folder, messages_get("NewFolder")); - } - - /* Update the icons - */ - if (entry == NULL) { - ro_gui_set_icon_string(dialog_folder, 1, messages_get("Folder")); - } else { - ro_gui_set_icon_string(dialog_folder, 1, entry->title); - } -} - -void ro_gui_hotlist_prepare_entry_dialog(bool selected) { - struct hotlist_entry *entry = NULL; - if (selected) entry = ro_gui_hotlist_first_selection(root.child_entry); - - /* Update the title - */ - dialog_entry_add = selected; - if (selected) { - ro_gui_set_window_title(dialog_entry, messages_get("EditLink")); - } else { - ro_gui_set_window_title(dialog_entry, messages_get("NewLink")); - } - - /* Update the icons - */ - if (entry == NULL) { - ro_gui_set_icon_string(dialog_entry, 1, messages_get("Link")); - ro_gui_set_icon_string(dialog_entry, 3, ""); - } else { - ro_gui_set_icon_string(dialog_entry, 1, entry->title); - ro_gui_set_icon_string(dialog_entry, 3, entry->url); - } -} - - /** - * Set all items to either selected or deselected + * Informs the hotlist that some content has been visited * - * \param selected the state to set all items to + * \param content the content visited */ -void ro_gui_hotlist_set_selected(bool selected) { - ro_gui_hotlist_selection_state(root.child_entry, selected, true); - menu_selection = false; +void hotlist_visited(struct content *content) { + if ((!content) || (!content->url) || (!hotlist_tree)) + return; + ro_gui_hotlist_visited(content, hotlist_tree, hotlist_tree->root); } /** - * Reset the statistics for selected entries + * Informs the hotlist that some content has been visited + * + * \param content the content visited + * \param tree the tree to find the URL data from + * \param node the node to update siblings and children of */ -void ro_gui_hotlist_reset_statistics(void) { - ro_gui_hotlist_invalidate_statistics(root.child_entry); +void ro_gui_hotlist_visited(struct content *content, struct tree *tree, + struct node *node) { + struct node_element *element; + + for (; node; node = node->next) { + if (!node->folder) { + element = tree_find_element(node, TREE_ELEMENT_URL); + if ((element) && (!strcmp(element->text, content->url))) { + element->user_data = ro_content_filetype(content); + element = tree_find_element(node, + TREE_ELEMENT_VISITS); + if (element) + element->user_data += 1; + element = tree_find_element(node, + TREE_ELEMENT_LAST_VISIT); + if (element) + element->user_data = time(NULL); + tree_update_URL_node(node); + tree_handle_node_changed(tree, node, true, false); + } + } + if (node->child) + ro_gui_hotlist_visited(content, tree, node->child); + } } /** - * Return the current number of selected items + * Prepares the folder dialog contents for a node * - * \param folders include folders in the selection count - * \return the number of selected items + * \param node the node to prepare the dialogue for, or NULL */ -int ro_gui_hotlist_get_selected(bool folders) { - return ro_gui_hotlist_selection_count(root.child_entry, folders); +void ro_gui_hotlist_prepare_folder_dialog(struct node *node) { + dialog_folder_node = node; + if (node) { + ro_gui_set_window_title(dialog_folder, messages_get("EditFolder")); + ro_gui_set_icon_string(dialog_folder, 1, node->data.text); + } else { + ro_gui_set_window_title(dialog_folder, messages_get("NewFolder")); + ro_gui_set_icon_string(dialog_folder, 1, messages_get("Folder")); + } } /** - * Set all items to either selected or deselected + * Prepares the entry dialog contents for a node * - * \param expand whether to expand (collapse otherwise) - * \param folders whether to update folders - * \param links whether to update links + * \param node the node to prepare the dialogue for, or NULL */ -void ro_gui_hotlist_set_expanded(bool expand, bool folders, bool links) { - ro_gui_hotlist_update_expansion(root.child_entry, false, folders, links, expand, !expand); +void ro_gui_hotlist_prepare_entry_dialog(struct node *node) { + struct node_element *element; + + dialog_entry_node = node; + if (node) { + ro_gui_set_window_title(dialog_entry, messages_get("EditLink")); + ro_gui_set_icon_string(dialog_entry, 1, node->data.text); + element = tree_find_element(node, TREE_ELEMENT_URL); + if (element) + ro_gui_set_icon_string(dialog_entry, 3, element->text); + else + ro_gui_set_icon_string(dialog_entry, 3, ""); + } else { + ro_gui_set_window_title(dialog_entry, messages_get("NewLink")); + ro_gui_set_icon_string(dialog_entry, 1, messages_get("Link")); + ro_gui_set_icon_string(dialog_entry, 3, ""); + } } /** - * Deletes any selected items + * Respond to a mouse click * - * \param selected the state to set all items to + * \param pointer the pointer state */ -void ro_gui_hotlist_delete_selected(void) { - struct hotlist_entry *entry; - while ((entry = ro_gui_hotlist_first_selection(root.child_entry)) != NULL) { - ro_gui_hotlist_delete_entry(entry, false); - } -} - void ro_gui_hotlist_dialog_click(wimp_pointer *pointer) { - struct hotlist_entry *entry = NULL; + struct node_element *element; + struct node *node; char *title = NULL; char *url = NULL; char *old_value; int icon = pointer->i; int close_icon, ok_icon; - bool folder; - bool add; url_func_result res; - /* Get our data - */ if (pointer->w == dialog_entry) { title = strip(ro_gui_get_icon_string(pointer->w, 1)); url = strip(ro_gui_get_icon_string(pointer->w, 3)); close_icon = 4; ok_icon = 5; - folder = false; - add = !dialog_entry_add; + node = dialog_entry_node; } else { title = strip(ro_gui_get_icon_string(pointer->w, 1)); close_icon = 2; ok_icon = 3; - folder = true; - add = !dialog_folder_add; + node = dialog_folder_node; } - /* Check for cancelling - */ if (icon == close_icon) { if (pointer->buttons == wimp_CLICK_SELECT) { ro_gui_dialog_close(pointer->w); xwimp_create_menu((wimp_menu *)-1, 0, 0); - return; - } - if (folder) { - ro_gui_hotlist_prepare_folder_dialog(dialog_folder_add); } else { - ro_gui_hotlist_prepare_entry_dialog(dialog_entry_add); + if (pointer->w == dialog_folder) + ro_gui_hotlist_prepare_folder_dialog(dialog_folder_node); + else + ro_gui_hotlist_prepare_entry_dialog(dialog_entry_node); } return; } - /* Check for ok - */ - if (icon != ok_icon) return; + if (icon != ok_icon) + return; /* Check we have valid values */ @@ -2514,95 +466,66 @@ void ro_gui_hotlist_dialog_click(wimp_pointer *pointer) { /* Update/insert our data */ - if (add) { - /* todo: insert at the selection place if hotlist_insert is set */ - ro_gui_hotlist_create_entry(title, url, folder ? 0 : 0xfaf, &root); + if (!node) { + if (pointer->w == dialog_folder) { + dialog_folder_node = tree_create_folder_node(hotlist_tree->root, + title); + node = dialog_folder_node; + } else { + dialog_entry_node = tree_create_URL_node(hotlist_tree->root, + title, url, 0xfaf, time(NULL), -1, 0); + node = dialog_entry_node; + } + tree_handle_node_changed(hotlist_tree, node, true, false); + ro_gui_tree_scroll_visible(hotlist_tree, &node->data); + tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, + 0, NODE_INSTEP, 16384); } else { - entry = ro_gui_hotlist_first_selection(root.child_entry); - if (entry == NULL) return; if (url) { - old_value = entry->url; - res = url_normalize(url, &entry->url); - if (res != URL_FUNC_OK) { - /** \todo use correct error message */ - warn_user("NoMemory", 0); - entry->url = old_value; - return; + element = tree_find_element(node, TREE_ELEMENT_URL); + if (element) { + old_value = element->text; + res = url_normalize(url, &element->text); + if (res != URL_FUNC_OK) { + warn_user("NoMemory", 0); + element->text = old_value; + return; + } + free(old_value); } - if (old_value) free(old_value); } if (title) { - old_value = entry->title; - entry->title = strdup(title); - if (!entry->title) { + old_value = node->data.text; + node->data.text = strdup(title); + if (!node->data.text) { warn_user("NoMemory", 0); - entry->title = old_value; + node->data.text = old_value; return; } free(old_value); } - hotlist_redraw_entry(entry, false); /* first for contracting size */ - ro_gui_hotlist_update_entry_size(entry); - hotlist_redraw_entry(entry, false); /* then for expanding size */ + tree_handle_node_changed(hotlist_tree, node, true, false); } - /* Close if we should - */ if (pointer->buttons == wimp_CLICK_SELECT) { xwimp_create_menu((wimp_menu *)-1, 0, 0); ro_gui_dialog_close(pointer->w); return; } - - /* Update our display - */ - if (folder) { - ro_gui_hotlist_prepare_folder_dialog(dialog_folder_add); - } else { - ro_gui_hotlist_prepare_entry_dialog(dialog_entry_add); - } + if (pointer->w == dialog_folder) + ro_gui_hotlist_prepare_folder_dialog(dialog_folder_node); + else + ro_gui_hotlist_prepare_entry_dialog(dialog_entry_node); } -int ro_gui_hotlist_help(int x, int y) { - struct hotlist_entry *entry; - wimp_window_state state; - int toolbar_height = 0; - int x_offset, y_offset; - - /* Return the dragging codes - */ - if (dragging) { - if (gui_current_drag_type == GUI_DRAG_HOTLIST_SELECT) return 6; - if (gui_current_drag_type == GUI_DRAG_HOTLIST_MOVE) return 7; - return -1; - } - - /* Get the toolbar height - */ - if (hotlist_toolbar) toolbar_height = hotlist_toolbar->height * 2; - - /* Get the window state to make everything relative - */ - state.w = hotlist_window; - wimp_get_window_state(&state); - - /* Create the relative positions - */ - x = x - state.visible.x0 - state.xscroll; - y = y - state.visible.y1 - state.yscroll + toolbar_height; - - /* Get the current entry - */ - entry = ro_gui_hotlist_find_entry(x, y, root.child_entry); - if (entry == NULL) return -1; - /* Return the relevant code - */ - x_offset = x - entry->x0; - y_offset = y - (entry->y0 + entry->height); - if ((x_offset < HOTLIST_LEAF_INSET) && (y_offset > -HOTLIST_LINE_HEIGHT)) { - if (entry->children == 0) return -1; - return (((entry->children == -1) ? 2 : 0) + ((entry->expanded) ? 1 : 0)); - } - return ((entry->children == -1) ? 5 : 4); +/** + * Attempts to process an interactive help message request + * + * \param x the x co-ordinate to give help for + * \param y the x co-ordinate to give help for + * \return the message code index + */ +int ro_gui_hotlist_help(int x, int y) { + return -1; } diff --git a/riscos/menus.c b/riscos/menus.c index cc258eb79..9ae2b8d8a 100644 --- a/riscos/menus.c +++ b/riscos/menus.c @@ -27,6 +27,7 @@ #include "netsurf/riscos/options.h" #include "netsurf/riscos/tinct.h" #include "netsurf/riscos/theme.h" +#include "netsurf/riscos/treeview.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" @@ -120,14 +121,14 @@ static wimp_MENU(3) link_menu = { static wimp_MENU(8) page_menu = { { "Page" }, 7,2,7,0, 200, 44, 0, { - { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "PageInfo" } }, - { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "Save" } }, - { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "SaveComp" } }, - { 0, (wimp_menu *)&export_menu, DEFAULT_FLAGS, { "Export" } }, - { 0, (wimp_menu *)&link_menu, DEFAULT_FLAGS, { "SaveURL" } }, - { wimp_MENU_GIVE_WARNING | wimp_MENU_SEPARATE, (wimp_menu *)1, DEFAULT_FLAGS, { "Print" } }, - { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "NewWindow" } }, - { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "ViewSrc" } } + { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "PageInfo" } }, + { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "Save" } }, + { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "SaveComp" } }, + { 0, (wimp_menu *)&export_menu, DEFAULT_FLAGS, { "Export" } }, + { 0, (wimp_menu *)&link_menu, DEFAULT_FLAGS, { "SaveURL" } }, + { wimp_MENU_GIVE_WARNING | wimp_MENU_SEPARATE, (wimp_menu *)1, DEFAULT_FLAGS, { "Print" } }, + { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "NewWindow" } }, + { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "ViewSrc" } } } }; @@ -187,9 +188,8 @@ static wimp_MENU(5) navigate_menu = { static wimp_MENU(3) image_menu = { { "Images" }, 7,2,7,0, 300, 44, 0, { - { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS | wimp_ICON_SHADED, { "ForeImg" } }, - { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "BackImg" } }, - { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "AnimImg" } } + { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS | wimp_ICON_SHADED, { "ForeImg" } }, + { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "BackImg" } }, } }; @@ -336,44 +336,30 @@ static wimp_MENU(3) hotlist_collapse = { } }; - -static wimp_MENU(3) hotlist_save = { - { "SaveSelect" }, 7,2,7,0, 200, 44, 0, - { - { wimp_MENU_GIVE_WARNING, (wimp_menu*)1, DEFAULT_FLAGS, { "URI" } }, - { wimp_MENU_GIVE_WARNING, (wimp_menu*)1, DEFAULT_FLAGS, { "URL" } }, - { wimp_MENU_LAST | wimp_MENU_GIVE_WARNING, (wimp_menu*)1, DEFAULT_FLAGS, { "HTML" } } - } -}; - /* Hotlist file submenu */ -static wimp_MENU(5) hotlist_file = { +static wimp_MENU(4) hotlist_file = { { "Hotlist" }, 7,2,7,0, 300, 44, 0, { { 0, (wimp_menu *)&hotlist_new, DEFAULT_FLAGS, { "New" } }, - { wimp_MENU_GIVE_WARNING, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Save" } }, { wimp_MENU_GIVE_WARNING | wimp_MENU_SEPARATE, (wimp_menu *)1, DEFAULT_FLAGS, { "Export" } }, { 0, (wimp_menu *)&hotlist_expand, DEFAULT_FLAGS, { "Expand" } }, { wimp_MENU_LAST, (wimp_menu *)&hotlist_collapse, DEFAULT_FLAGS, { "Collapse" } } } }; - /* Hotlist file submenu */ -static wimp_MENU(5) hotlist_select = { +static wimp_MENU(4) hotlist_select = { { "Selection" }, 7,2,7,0, 300, 44, 0, { - { wimp_MENU_GIVE_WARNING, (wimp_menu *)&hotlist_save, DEFAULT_FLAGS, { "SaveSelect" } }, - { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "Edit" } }, - { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Launch" } }, - { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Delete" } }, - { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "ResetUsage" } } + { wimp_MENU_GIVE_WARNING, (wimp_menu *)1, DEFAULT_FLAGS, { "Edit" } }, + { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Launch" } }, + { 0, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "Delete" } }, + { wimp_MENU_LAST, wimp_NO_SUB_MENU, DEFAULT_FLAGS, { "ResetUsage" } } } }; - /* Hotlist menu */ static wimp_MENU(4) hotlist_root = { @@ -501,7 +487,6 @@ static wimp_menu *hotlist_new_menu = (wimp_menu *)&hotlist_new; static wimp_menu *hotlist_expand_menu = (wimp_menu *)&hotlist_expand; static wimp_menu *hotlist_collapse_menu = (wimp_menu *)&hotlist_collapse; static wimp_menu *hotlist_file_menu = (wimp_menu *)&hotlist_file; -static wimp_menu *hotlist_save_menu = (wimp_menu *)&hotlist_save; static wimp_menu *hotlist_select_menu = (wimp_menu *)&hotlist_select; @@ -535,7 +520,6 @@ void ro_gui_menus_init(void) translate_menu(hotlist_expand_menu); translate_menu(hotlist_collapse_menu); translate_menu(hotlist_file_menu); - translate_menu(hotlist_save_menu); translate_menu(hotlist_select_menu); translate_menu(toolbar_menu); @@ -620,8 +604,8 @@ void build_languages_menu(void) languages_menu->gap = 0; while (context != -1) { - e = xosgbpb_dir_entries_info("<NetSurf$Dir>.Resources", (osgbpb_info_list*)&info, 1, context, sizeof(info), 0, &read_count, &context); - + e = xosgbpb_dir_entries_info("<NetSurf$Dir>.Resources", (osgbpb_info_list*)&info, 1, + context, sizeof(info), 0, &read_count, &context); if (e) die(e->errmess); @@ -665,9 +649,6 @@ void ro_gui_create_menu(wimp_menu *menu, int x, int y, struct gui_window *g) wimp_window_state state; os_error *error; - current_menu = menu; - current_menu_x = x; - current_menu_y = y; current_gui = g; if (menu == browser_menu) { @@ -692,7 +673,7 @@ void ro_gui_create_menu(wimp_menu *menu, int x, int y, struct gui_window *g) g->bw->current_content, doc_x, doc_y); } - if (!hotlist_window) + if (!hotlist_tree) browser_utilities_menu->entries[0].icon_flags |= wimp_ICON_SHADED; if (gui_menu_object_box) @@ -728,6 +709,10 @@ void ro_gui_create_menu(wimp_menu *menu, int x, int y, struct gui_window *g) LOG(("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); + } else { + current_menu = menu; + current_menu_x = x; + current_menu_y = y; } } @@ -759,6 +744,7 @@ void ro_gui_menu_selection(wimp_selection *selection) { struct toolbar_icon *icon; struct toolbar_icon *next; + struct node *node; char url[80]; wimp_pointer pointer; wimp_window_state state; @@ -806,11 +792,11 @@ void ro_gui_menu_selection(wimp_selection *selection) if ((height != current_toolbar->height) && (current_gui)) ro_gui_window_update_dimensions(current_gui, height - current_toolbar->height); - if ((height != current_toolbar->height) && +/* if ((height != current_toolbar->height) && (current_toolbar == hotlist_toolbar)) { xwimp_force_redraw(hotlist_window, 0, -16384, 16384, 16384); } - ro_gui_menu_prepare_theme(); +*/ ro_gui_menu_prepare_theme(); break; case 1: /* Toolbars-> */ @@ -855,46 +841,39 @@ void ro_gui_menu_selection(wimp_selection *selection) switch (selection->items[1]) { case 0: /* New */ break; - case 1: /* Save */ - ro_gui_hotlist_save(); + case 1: /* Export */ break; - case 2: /* Export */ + case 2: /* Expand */ + tree_handle_expansion(hotlist_tree, hotlist_tree->root, true, + (selection->items[2] != 2), (selection->items[2] != 1)); break; - case 3: /* Expand */ - ro_gui_hotlist_set_expanded(true, - (selection->items[2] != 2), - (selection->items[2] != 1)); - break; - case 4: /* Collapse */ - ro_gui_hotlist_set_expanded(false, - (selection->items[2] != 2), - (selection->items[2] != 1)); + case 3: /* Collapse */ + tree_handle_expansion(hotlist_tree, hotlist_tree->root, false, + (selection->items[2] != 2), (selection->items[2] != 1)); break; } break; case 1: /* Selection-> */ switch (selection->items[1]) { - case 0: /* Save */ - break; - case 1: /* Edit title-> */ + case 0: /* Edit-> */ break; - case 2: /* Launch */ - ro_gui_hotlist_keypress(wimp_KEY_RETURN); + case 1: /* Launch */ + ro_gui_tree_launch_selected(hotlist_tree); break; - case 3: /* Delete */ - ro_gui_hotlist_delete_selected(); + case 2: /* Delete */ + tree_delete_selected_nodes(hotlist_tree, hotlist_tree->root); break; - case 4: /* Reset usage */ - ro_gui_hotlist_reset_statistics(); + case 3: /* Reset usage */ + tree_reset_URL_nodes(hotlist_tree, hotlist_tree->root, true); break; } break; case 2: /* Select all */ - ro_gui_hotlist_set_selected(true); + ro_gui_tree_keypress(1, hotlist_tree); ro_gui_menu_prepare_hotlist(); break; case 3: /* Clear */ - ro_gui_hotlist_set_selected(false); + ro_gui_tree_keypress(26, hotlist_tree); ro_gui_menu_prepare_hotlist(); break; } @@ -924,7 +903,8 @@ void ro_gui_menu_selection(wimp_selection *selection) case 5: /* Print */ break; case 6: /* New window */ - browser_window_create(current_gui->bw->current_content->url, current_gui->bw, 0); + browser_window_create(current_gui->bw->current_content->url, + current_gui->bw, 0); break; case 7: /* Page source */ ro_gui_view_source(c); @@ -971,7 +951,8 @@ void ro_gui_menu_selection(wimp_selection *selection) switch (selection->items[1]) { case 0: /* Home */ if (option_homepage_url && option_homepage_url[0]) { - browser_window_go_post(current_gui->bw, option_homepage_url, 0, 0, true, 0); + browser_window_go_post(current_gui->bw, option_homepage_url, + 0, 0, true, 0); } else { snprintf(url, sizeof url, "file:/<NetSurf$Dir>/Docs/intro_%s", @@ -1009,10 +990,6 @@ void ro_gui_menu_selection(wimp_selection *selection) current_gui->option.background_images = !current_gui->option.background_images; break; - case 2: - current_gui->option.animate_images = - !current_gui->option.animate_images; - break; } ro_gui_menu_prepare_images(); gui_window_redraw_window(current_gui); @@ -1073,8 +1050,15 @@ void ro_gui_menu_selection(wimp_selection *selection) case 0: /* Hotlist -> */ switch (selection->items[2]) { case 0: /* Add to hotlist */ - ro_gui_hotlist_add(current_gui->title, - current_gui->bw->current_content); + node = tree_create_URL_node(hotlist_tree->root, + messages_get(current_gui->title), + current_gui->bw->current_content->url, + ro_content_filetype(current_gui->bw->current_content), + time(NULL), -1, 0); + tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, 0, + NODE_INSTEP, 16384); + tree_handle_node_changed(hotlist_tree, node, false, true); + ro_gui_tree_scroll_visible(hotlist_tree, &node->data); break; case 1: /* Show hotlist */ ro_gui_hotlist_show(); @@ -1084,7 +1068,8 @@ void ro_gui_menu_selection(wimp_selection *selection) case 1: /* Window -> */ switch (selection->items[2]) { case 0: - ro_gui_screen_size(&option_window_screen_width, &option_window_screen_height); + ro_gui_screen_size(&option_window_screen_width, + &option_window_screen_height); state.w = current_gui->bw->window->window; error = xwimp_get_window_state(&state); if (error) { @@ -1157,9 +1142,10 @@ void ro_gui_menu_selection(wimp_selection *selection) current_menu_x, current_menu_y, current_gui); } else { - if (current_menu == hotlist_menu) { + if (current_menu == hotlist_menu) ro_gui_hotlist_menu_closed(); - } + current_menu = NULL; + current_gui = NULL; } } @@ -1394,71 +1380,59 @@ void ro_gui_menu_browser_warning(wimp_message_menu_warning *warning) void ro_gui_menu_hotlist_warning(wimp_message_menu_warning *warning) { - os_error *error = 0; + os_error *error = NULL; + struct node *node; switch (warning->selection.items[0]) { - case 0: /* Hotlist-> */ - switch (warning->selection.items[1]) { - case 0: /* New-> */ - hotlist_insert = true; - switch (warning->selection.items[2]) { - case 0: /* Folder */ - ro_gui_hotlist_prepare_folder_dialog(false); - error = xwimp_create_sub_menu( - (wimp_menu *) dialog_folder, - warning->pos.x, warning->pos.y); - break; - case 1: /* Entry */ - ro_gui_hotlist_prepare_entry_dialog(false); - error = xwimp_create_sub_menu( - (wimp_menu *) dialog_entry, - warning->pos.x, warning->pos.y); - } - break; - case 2: /* Export-> */ - ro_gui_save_open(GUI_SAVE_HOTLIST_EXPORT_HTML, 0, true, - warning->pos.x, warning->pos.y, 0, false); - break; - } - break; - case 1: /* Selection-> */ - switch (warning->selection.items[1]) { - case -1: /* Root */ - ro_gui_menu_prepare_hotlist(); - error = xwimp_create_sub_menu(hotlist_select_menu, - warning->pos.x, warning->pos.y); - break; - case 0: /* Save-> */ - switch (warning->selection.items[2]) { - case -1: /* No sub-menu */ - ro_gui_menu_prepare_hotlist(); - error = xwimp_create_sub_menu(hotlist_save_menu, - warning->pos.x, warning->pos.y); - break; - case 1: /* URI */ - break; - case 2: /* URL */ - break; - case 3: /* HTML */ - break; + case 0: /* Hotlist-> */ + switch (warning->selection.items[1]) { + case 0: /* New-> */ + switch (warning->selection.items[2]) { + case 0: /* Folder */ + ro_gui_hotlist_prepare_folder_dialog(NULL); + error = xwimp_create_sub_menu( + (wimp_menu *) dialog_folder, + warning->pos.x, warning->pos.y); + break; + case 1: /* Entry */ + ro_gui_hotlist_prepare_entry_dialog(NULL); + error = xwimp_create_sub_menu( + (wimp_menu *) dialog_entry, + warning->pos.x, warning->pos.y); + break; + } + break; + case 1: /* Export-> */ + ro_gui_save_open(GUI_SAVE_HOTLIST_EXPORT_HTML, 0, true, + warning->pos.x, warning->pos.y, 0, false); + break; } break; - case 1: /* Edit-> */ - hotlist_insert = true; - if (ro_gui_hotlist_get_selected(false) == 0) { - ro_gui_hotlist_prepare_folder_dialog(true); - error = xwimp_create_sub_menu( - (wimp_menu *) dialog_folder, - warning->pos.x, warning->pos.y); - } else { - ro_gui_hotlist_prepare_entry_dialog(true); - error = xwimp_create_sub_menu( - (wimp_menu *) dialog_entry, - warning->pos.x, warning->pos.y); + case 1: /* Selection-> */ + switch (warning->selection.items[1]) { + case -1: /* Root */ + ro_gui_menu_prepare_hotlist(); + error = xwimp_create_sub_menu(hotlist_select_menu, + warning->pos.x, warning->pos.y); + break; + case 0: /* Edit-> */ + node = tree_get_selected_node(hotlist_tree->root); + if (!node) + break; + if (node->folder) { + ro_gui_hotlist_prepare_folder_dialog(node); + error = xwimp_create_sub_menu( + (wimp_menu *) dialog_folder, + warning->pos.x, warning->pos.y); + } else { + ro_gui_hotlist_prepare_entry_dialog(node); + error = xwimp_create_sub_menu( + (wimp_menu *) dialog_entry, + warning->pos.x, warning->pos.y); + } + break; } break; - } - break; } if (error) { @@ -1535,6 +1509,7 @@ void ro_gui_prepare_navigate(struct gui_window *gui) { ro_gui_set_icon_shaded_state(t->toolbar_handle, ICON_TOOLBAR_FORWARD, true); ro_gui_set_icon_shaded_state(t->toolbar_handle, ICON_TOOLBAR_HISTORY, true); } + ro_gui_set_icon_shaded_state(t->toolbar_handle, ICON_TOOLBAR_BOOKMARK, !hotlist_tree); } /* Update the stop/refresh icons/buttons @@ -1611,7 +1586,6 @@ static void ro_gui_menu_prepare_images(void) { browser_image_menu->entries[1].menu_flags &= ~wimp_MENU_TICKED; if (current_gui->option.background_images) browser_image_menu->entries[1].menu_flags |= wimp_MENU_TICKED; browser_image_menu->entries[2].menu_flags &= ~wimp_MENU_TICKED; - if (current_gui->option.animate_images) browser_image_menu->entries[2].menu_flags |= wimp_MENU_TICKED; } @@ -1800,49 +1774,57 @@ void ro_gui_menu_prepare_scale(void) { ro_gui_current_zoom_gui = current_gui; } + /** * Update hotlist menu (all of) */ void ro_gui_menu_prepare_hotlist(void) { - int selection; - int selection_full; - selection = ro_gui_hotlist_get_selected(false); - selection_full = ro_gui_hotlist_get_selected(true); + os_error *error; + bool reopen = false; + bool selection = false; + struct node *single = NULL; + + if (hotlist_tree->root->child) { + single = tree_get_selected_node(hotlist_tree->root->child); + selection = tree_has_selection(hotlist_tree->root->child); + } if (hotlist_toolbar) { ro_gui_set_icon_shaded_state(hotlist_toolbar->toolbar_handle, - ICON_TOOLBAR_DELETE, (selection_full == 0)); + ICON_TOOLBAR_DELETE, !selection); ro_gui_set_icon_shaded_state(hotlist_toolbar->toolbar_handle, - ICON_TOOLBAR_LAUNCH, (selection == 0)); + ICON_TOOLBAR_LAUNCH, !selection); } - if (selection_full == 0) { - hotlist_menu->entries[1].icon_flags |= wimp_ICON_SHADED; - hotlist_menu->entries[3].icon_flags |= wimp_ICON_SHADED; - } else { + + if (selection) { + reopen |= (hotlist_menu->entries[1].icon_flags & wimp_ICON_SHADED); hotlist_menu->entries[1].icon_flags &= ~wimp_ICON_SHADED; hotlist_menu->entries[3].icon_flags &= ~wimp_ICON_SHADED; - } - if (selection == 0) { - hotlist_select_menu->entries[2].icon_flags |= wimp_ICON_SHADED; - hotlist_select_menu->entries[4].icon_flags |= wimp_ICON_SHADED; } else { - hotlist_select_menu->entries[2].icon_flags &= ~wimp_ICON_SHADED; - hotlist_select_menu->entries[4].icon_flags &= ~wimp_ICON_SHADED; + reopen |= !(hotlist_menu->entries[1].icon_flags & wimp_ICON_SHADED); + hotlist_menu->entries[1].icon_flags |= wimp_ICON_SHADED; + hotlist_menu->entries[3].icon_flags |= wimp_ICON_SHADED; } - if (selection_full != 1) { - hotlist_select_menu->entries[1].icon_flags |= wimp_ICON_SHADED; + + if (single) { + reopen |= (hotlist_select_menu->entries[0].icon_flags & wimp_ICON_SHADED); + hotlist_select_menu->entries[0].icon_flags &= ~wimp_ICON_SHADED; } else { - hotlist_select_menu->entries[1].icon_flags &= ~wimp_ICON_SHADED; + reopen |= !(hotlist_select_menu->entries[0].icon_flags & wimp_ICON_SHADED); + hotlist_select_menu->entries[0].icon_flags |= wimp_ICON_SHADED; } - if (selection != 1) { - hotlist_save_menu->entries[0].icon_flags |= wimp_ICON_SHADED; - hotlist_save_menu->entries[1].icon_flags |= wimp_ICON_SHADED; - } else { - hotlist_save_menu->entries[0].icon_flags &= ~wimp_ICON_SHADED; - hotlist_save_menu->entries[1].icon_flags &= ~wimp_ICON_SHADED; + + if ((reopen) && (current_menu == hotlist_menu)) { + error = xwimp_create_menu(hotlist_menu, 0, 0); + if (error) { + LOG(("xwimp_create_menu: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("MenuError", error->errmess); + } } } + /** * Update the Interactive Help status * @@ -1926,8 +1908,11 @@ void ro_gui_menu_objectinfo(wimp_message_menu_warning *warning) const char *mime = "-"; os_error *error; - sprintf(icon_buf, "file_%x", + sprintf(icon_buf, "file_%.3x", ro_content_filetype(gui_menu_object_box->object)); + if (!ro_gui_wimp_sprite_exists(icon_buf)) + sprintf(icon_buf, "file_xxx"); + if (gui_menu_object_box->object->url) url = gui_menu_object_box->object->url; if (gui_menu_object_box->href) diff --git a/riscos/options.h b/riscos/options.h index 932486569..7ec661451 100644 --- a/riscos/options.h +++ b/riscos/options.h @@ -31,7 +31,6 @@ extern bool option_toolbar_show_status; extern bool option_toolbar_show_buttons; extern bool option_toolbar_show_address; extern bool option_toolbar_show_throbber; -extern bool option_animate_images; extern int option_window_x; extern int option_window_y; extern int option_window_width; @@ -88,7 +87,6 @@ bool option_toolbar_show_status = true; \ bool option_toolbar_show_buttons = true; \ bool option_toolbar_show_address = true; \ bool option_toolbar_show_throbber = true; \ -bool option_animate_images = true; \ int option_window_x = 0; \ int option_window_y = 0; \ int option_window_width = 0; \ @@ -145,7 +143,6 @@ bool option_font_ufont = false; { "toolbar_show_buttons", OPTION_BOOL, &option_toolbar_show_buttons }, \ { "toolbar_show_address", OPTION_BOOL, &option_toolbar_show_address }, \ { "toolbar_show_throbber", OPTION_BOOL, &option_toolbar_show_throbber }, \ -{ "animate_images", OPTION_BOOL, &option_animate_images }, \ { "window_x", OPTION_INTEGER, &option_window_x }, \ { "window_y", OPTION_INTEGER, &option_window_y }, \ { "window_width", OPTION_INTEGER, &option_window_width }, \ diff --git a/riscos/save.c b/riscos/save.c index 69d590e86..08d1f9fa7 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -309,7 +309,12 @@ void ro_gui_save_datasave_ack(wimp_message *message) return; break; case GUI_SAVE_HOTLIST_EXPORT_HTML: - ro_gui_hotlist_save_as(path); + if (!options_save_hotlist(hotlist_tree, path)) + return; + error = xosfile_set_type(path, 0xfaf); + if (error) + LOG(("xosfile_set_type: 0x%x: %s", + error->errnum, error->errmess)); break; } diff --git a/riscos/treeview.c b/riscos/treeview.c new file mode 100644 index 000000000..c8db5a21e --- /dev/null +++ b/riscos/treeview.c @@ -0,0 +1,1183 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (implementation). + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <swis.h> +#include <time.h> +#include "oslib/colourtrans.h" +#include "oslib/dragasprite.h" +#include "oslib/osbyte.h" +#include "oslib/osspriteop.h" +#include "oslib/wimp.h" +#include "netsurf/desktop/tree.h" +#include "netsurf/riscos/gui.h" +#include "netsurf/riscos/tinct.h" +#include "netsurf/riscos/treeview.h" +#include "netsurf/riscos/wimp.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/messages.h" +#include "netsurf/utils/utils.h" + +#define TREE_EXPAND 0 +#define TREE_COLLAPSE 1 + + +static bool ro_gui_tree_initialise_sprite(const char *name, int number); +static void ro_gui_tree_launch_selected_node(struct node *node, bool all); +static bool ro_gui_tree_launch_node(struct node *node); + +/* an array of sprite addresses for Tinct */ +static char *ro_gui_tree_sprites[2]; + +/* origin adjustment */ +static int ro_gui_tree_origin_x; +static int ro_gui_tree_origin_y; + +/* element drawing */ +static wimp_icon ro_gui_tree_icon; +static char ro_gui_tree_icon_validation[24]; +static char ro_gui_tree_icon_null[] = "\0"; + +/* dragging information */ +static struct tree *ro_gui_tree_current_drag_tree; +static wimp_mouse_state ro_gui_tree_current_drag_buttons; + +/* editing information */ +static wimp_icon_create ro_gui_tree_edit_icon; + +/* dragging information */ +static char ro_gui_tree_drag_name[12]; + + +/** + * Performs any initialisation for tree rendering + */ +bool ro_gui_tree_initialise(void) { + if (ro_gui_tree_initialise_sprite("expand", TREE_EXPAND) || + ro_gui_tree_initialise_sprite("collapse", TREE_COLLAPSE)) + return false; + + ro_gui_tree_edit_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED | + wimp_ICON_VCENTRED | wimp_ICON_FILLED | wimp_ICON_BORDER | + (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT) | + (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | + (wimp_BUTTON_WRITABLE << wimp_ICON_BUTTON_TYPE_SHIFT); + ro_gui_tree_edit_icon.icon.data.indirected_text.validation = + ro_gui_tree_icon_null; + ro_gui_tree_edit_icon.icon.data.indirected_text.size = 256; + + return true; +} + + +/** + * Initialise a sprite for use with Tinct + * + * \param name the name of the sprite + * \param number the sprite cache number + * \return whether an error occurred during initialisation + */ +bool ro_gui_tree_initialise_sprite(const char *name, int number) { + char icon_name[12]; + os_error *error; + + sprintf(icon_name, "tr_%s", name); + error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, + (osspriteop_id)icon_name, + (osspriteop_header **)&ro_gui_tree_sprites[number]); + if (error) { + warn_user("MiscError", error->errmess); + LOG(("Failed to find sprite 'tr_%s'", name)); + return true; + } + return false; +} + + +/** + * Informs the current window manager that an area requires updating. + * + * \param tree the tree that is requesting a redraw + * \param x the x co-ordinate of the redraw area + * \param y the y co-ordinate of the redraw area + * \param width the width of the redraw area + * \param height the height of the redraw area + */ +void tree_redraw_area(struct tree *tree, int x, int y, int width, int height) { + os_error *error; + + assert(tree); + assert(tree->handle); + + error = xwimp_force_redraw((wimp_w)tree->handle, tree->offset_x + x - 2, + -tree->offset_y - y - height, tree->offset_x + x + width + 4, + -tree->offset_y - y); + if (error) { + LOG(("xwimp_force_redraw: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } +} + + +/** + * Draws a line. + * + * \param tree the tree to draw a line for + * \param x the x co-ordinate + * \param x the y co-ordinate + * \param x the width of the line + * \param x the height of the line + */ +void tree_draw_line(struct tree *tree, int x, int y, int width, int height) { + os_error *error; + + assert(tree); + + error = xcolourtrans_set_gcol((os_colour)0x88888800, 0, os_ACTION_OVERWRITE, + 0, 0); + if (error) { + LOG(("xcolourtrans_set_gcol: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("MiscError", error->errmess); + return; + } + error = xos_plot(os_MOVE_TO, ro_gui_tree_origin_x + x, + ro_gui_tree_origin_y - y); + if (!error) + xos_plot(os_PLOT_TO, ro_gui_tree_origin_x + x + width, + ro_gui_tree_origin_y - y - height); + if (error) { + LOG(("xos_plot: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("MiscError", error->errmess); + return; + } +} + + +/** + * Draws an element, including any expansion icons + * + * \param tree the tree to draw an element for + * \param element the element to draw + */ +void tree_draw_node_element(struct tree *tree, struct node_element *element) { + os_error *error; + int temp; + + assert(tree); + assert(element); + assert(element->parent); + + ro_gui_tree_icon.flags = wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED | + (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); + ro_gui_tree_icon.extent.x0 = tree->offset_x + element->box.x; + ro_gui_tree_icon.extent.y1 = -tree->offset_y - element->box.y; + ro_gui_tree_icon.extent.x1 = tree->offset_x + element->box.x + + element->box.width; + ro_gui_tree_icon.extent.y0 = -tree->offset_y - element->box.y - + element->box.height; + if (&element->parent->data == element) { + if (element->parent->selected) + ro_gui_tree_icon.flags |= wimp_ICON_SELECTED; + ro_gui_tree_icon.flags |= (wimp_COLOUR_BLACK << + wimp_ICON_FG_COLOUR_SHIFT); + } else { + ro_gui_tree_icon.flags |= (wimp_COLOUR_DARK_GREY << + wimp_ICON_FG_COLOUR_SHIFT); + } + + switch (element->type) { + case NODE_ELEMENT_TEXT_PLUS_SPRITE: + assert(element->sprite); + + ro_gui_tree_icon.flags |= wimp_ICON_TEXT | wimp_ICON_SPRITE; + ro_gui_tree_icon.data.indirected_text_and_sprite.text = + ro_gui_tree_icon_null; + ro_gui_tree_icon.data.indirected_text_and_sprite.validation = + ro_gui_tree_icon_validation; + ro_gui_tree_icon.data.indirected_text_and_sprite.size = 1; + if (element->parent->expanded) { + sprintf(ro_gui_tree_icon_validation, "S%s", + element->sprite->expanded_name); + } else { + sprintf(ro_gui_tree_icon_validation, "S%s", + element->sprite->name); + } + temp = ro_gui_tree_icon.extent.x1; + ro_gui_tree_icon.extent.x1 = ro_gui_tree_icon.extent.x0 + + NODE_INSTEP; + error = xwimp_plot_icon(&ro_gui_tree_icon); + if (error) { + LOG(("xwimp_plot_icon: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + ro_gui_tree_icon.extent.x0 = ro_gui_tree_icon.extent.x1; + ro_gui_tree_icon.extent.x1 = temp; + ro_gui_tree_icon.flags &= ~wimp_ICON_SPRITE; + + case NODE_ELEMENT_TEXT: + assert(element->text); + + if (element == tree->editing) + return; + + if (ro_gui_tree_icon.flags & wimp_ICON_SELECTED) + ro_gui_tree_icon.flags |= wimp_ICON_FILLED; + ro_gui_tree_icon.flags |= wimp_ICON_TEXT; + ro_gui_tree_icon.data.indirected_text.text = + element->text; + ro_gui_tree_icon.data.indirected_text.validation = + ro_gui_tree_icon_null; + ro_gui_tree_icon.data.indirected_text.size = + strlen(element->text); + break; + case NODE_ELEMENT_SPRITE: + assert(element->sprite); + + ro_gui_tree_icon.flags |= wimp_ICON_SPRITE; + ro_gui_tree_icon.data.indirected_sprite.id = + (osspriteop_id)element->sprite->name; + ro_gui_tree_icon.data.indirected_sprite.area = + element->sprite->area; + ro_gui_tree_icon.data.indirected_sprite.size = + strlen(element->sprite->name); + break; + } + + error = xwimp_plot_icon(&ro_gui_tree_icon); + if (error) { + LOG(("xwimp_plot_icon: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } +} + + +/** + * Draws an elements expansion icon + * + * \param tree the tree to draw the expansion for + * \param element the element to draw the expansion for + */ +void tree_draw_node_expansion(struct tree *tree, struct node *node) { + unsigned int type; + + assert(tree); + assert(node); + + if ((node->child) || (node->data.next)) { + if (node->expanded) { + type = TREE_COLLAPSE; + } else { + type = TREE_EXPAND; + } + _swix(Tinct_Plot, _IN(2) | _IN(3) | _IN(4) | _IN(7), + ro_gui_tree_sprites[type], + ro_gui_tree_origin_x + node->box.x - + (NODE_INSTEP / 2) - 8, + ro_gui_tree_origin_y - node->box.y - + (TREE_TEXT_HEIGHT / 2) - 8, + tinct_BILINEAR_FILTER); + + } +} + + +/** + * Sets the origin variables to the correct values for a specified tree + * + * \param tree the tree to set the origin for + */ +void tree_initialise_redraw(struct tree *tree) { + os_error *error; + wimp_window_state state; + + assert(tree->handle); + + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + + ro_gui_tree_origin_x = state.visible.x0 - state.xscroll + tree->offset_x; + ro_gui_tree_origin_y = state.visible.y1 - state.yscroll - tree->offset_y; +} + + +/** + * Recalculates the dimensions of a node element. + * + * \param element the element to recalculate + */ +void tree_recalculate_node_element(struct node_element *element) { + os_error *error; + int sprite_width; + int sprite_height; + osspriteop_flags flags; + + assert(element); + + switch (element->type) { + case NODE_ELEMENT_TEXT_PLUS_SPRITE: + assert(element->sprite); + case NODE_ELEMENT_TEXT: + assert(element->text); + + error = xwimptextop_string_width(element->text, + strlen(element->text), + &element->box.width); + if (error) { + LOG(("xwimptextop_string_width: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + element->box.width += 16; + element->box.height = TREE_TEXT_HEIGHT; + if (element->type == NODE_ELEMENT_TEXT_PLUS_SPRITE) + element->box.width += NODE_INSTEP; + break; + case NODE_ELEMENT_SPRITE: + assert(element->sprite); + + flags = ((int)element->sprite->area == 1) ? + osspriteop_SYSTEM_AREA : + osspriteop_USER_AREA; + error = xosspriteop_read_sprite_info(flags, + element->sprite->area, + (osspriteop_id)element->sprite->name, + &sprite_width, &sprite_height, 0, 0); + if (error) { + LOG(("xosspriteop_read_sprite_info: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + element->box.width = sprite_width * 2; + element->box.height = sprite_height * 2; + if (element->box.height < TREE_TEXT_HEIGHT) + element->box.height = TREE_TEXT_HEIGHT; + break; + } +} + + +/** + * Sets a node element as having a specific sprite. + * + * \param node the node to update + * \param sprite the sprite to use + * \param selected the expanded sprite name to use + */ +void tree_set_node_sprite(struct node *node, const char *sprite, + const char *expanded) { + assert(node); + assert(sprite); + assert(expanded); + assert(node->data.type != NODE_ELEMENT_SPRITE); + + node->data.sprite = calloc(sizeof(struct node_sprite), 1); + if (!node->data.sprite) return; + node->data.type = NODE_ELEMENT_TEXT_PLUS_SPRITE; + node->data.sprite->area = (osspriteop_area *)1; + sprintf(node->data.sprite->name, sprite); + sprintf(node->data.sprite->expanded_name, expanded); +} + + +/** + * Sets a node element as having a folder sprite + * + * \param node the node to update + */ +void tree_set_node_sprite_folder(struct node *node) { + assert(node->folder); + + tree_set_node_sprite(node, "small_dir", "small_diro"); +} + + +/** + * Updates the node details for a URL node. + * The internal node dimensions are not updated. + * + * \param node the node to update + */ +void tree_update_URL_node(struct node *node) { + struct node_element *element; + char buffer[256]; + + assert(node); + + element = tree_find_element(node, TREE_ELEMENT_URL); + if (element) { + sprintf(buffer, "small_%.3x", element->user_data); + if (ro_gui_wimp_sprite_exists(buffer)) + tree_set_node_sprite(node, buffer, buffer); + else + tree_set_node_sprite(node, "small_xxx", "small_xxx"); + } + + element = tree_find_element(node, TREE_ELEMENT_ADDED); + if (element) { + if (element->text) { + free(element->text); + element->text = NULL; + } + if (element->user_data > 0) { + snprintf(buffer, 256, messages_get("TreeAdded"), + ctime((time_t *)&element->user_data)); + } else { + snprintf(buffer, 256, messages_get("TreeAdded"), + messages_get("TreeUnknown")); + } + element->text = strdup(buffer); + } + + element = tree_find_element(node, TREE_ELEMENT_LAST_VISIT); + if (element) { + if (element->text) { + free(element->text); + element->text = NULL; + } + if (element->user_data > 0) { + snprintf(buffer, 256, messages_get("TreeLast"), + ctime((time_t *)&element->user_data)); + } else { + snprintf(buffer, 256, messages_get("TreeLast"), + messages_get("TreeUnknown")); + } + element->text = strdup(buffer); + } + + element = tree_find_element(node, TREE_ELEMENT_VISITS); + if (element) { + if (element->text) { + free(element->text); + element->text = NULL; + } + snprintf(buffer, 256, messages_get("TreeVisits"), + element->user_data); + element->text = strdup(buffer); + } + +} + + +/** + * Updates the tree owner following a tree resize + * + * \param tree the tree to update the owner of + */ +void tree_resized(struct tree *tree) { + os_error *error; + wimp_window_state state; + + assert(tree->handle); + + + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + if (state.flags & wimp_WINDOW_OPEN) + ro_gui_tree_open((wimp_open *)&state, tree); +} + + +/** + * Redraws a tree window + * + * \param redraw the area to redraw + * \param tree the tree to redraw + */ +void ro_gui_tree_redraw(wimp_draw *redraw, struct tree *tree) { + osbool more; + int clip_x0, clip_x1, clip_y0, clip_y1, origin_x, origin_y; + + more = wimp_redraw_window(redraw); + while (more) { + clip_x0 = redraw->clip.x0; + clip_y0 = redraw->clip.y0; + clip_x1 = redraw->clip.x1; + clip_y1 = redraw->clip.y1; + origin_x = redraw->box.x0 - redraw->xscroll; + origin_y = redraw->box.y1 - redraw->yscroll; + tree_draw(tree, clip_x0 - origin_x - tree->offset_x, + origin_y - clip_y1 - tree->offset_y, + clip_x1 - clip_x0, clip_y1 - clip_y0); + more = wimp_get_rectangle(redraw); + } +} + + +/** + * Handles a mouse click for a tree + * + * \param pointer the pointer state + * \param tree the tree to handle a click for + * \return whether the click was handled# + */ +bool ro_gui_tree_click(wimp_pointer *pointer, struct tree *tree) { + bool furniture; + struct node *node; + struct node_element *element; + int x, y; + int alt_pressed = 0; + wimp_window_state state; + wimp_caret caret; + wimp_drag drag; + wimp_auto_scroll_info scroll; + os_error *error; + os_box box = { pointer->pos.x - 34, pointer->pos.y - 34, + pointer->pos.x + 34, pointer->pos.y + 34 }; + + assert(tree); + assert(tree->root); + + /* gain the input focus when required */ + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + error = xwimp_get_caret_position(&caret); + if (error) + LOG(("xwimp_get_caret_position: 0x%x: %s", + error->errnum, error->errmess)); + if (((pointer->buttons == (wimp_CLICK_SELECT << 8)) || + (pointer->buttons == (wimp_CLICK_ADJUST << 8))) && + (caret.w != state.w)) { + error = xwimp_set_caret_position((wimp_w)tree->handle, -1, -100, + -100, 32, -1); + if (error) + LOG(("xwimp_set_caret_position: 0x%x: %s", + error->errnum, error->errmess)); + } + + if (!tree->root->child) + return true; + + tree_initialise_redraw(tree); + x = pointer->pos.x - ro_gui_tree_origin_x; + y = ro_gui_tree_origin_y - pointer->pos.y; + element = tree_get_node_element_at(tree->root->child, x, y, &furniture); + node = element->parent; + + + /* stop editing for anything but a drag */ + if ((tree->editing) && (pointer->i != tree->edit_handle) && + (pointer->buttons != (wimp_CLICK_SELECT << 4))) + ro_gui_tree_stop_edit(tree); + + /* handle a menu click */ + if (pointer->buttons == wimp_CLICK_MENU) { + if ((!element) || (!tree->root->child) || + (tree_has_selection(tree->root->child))) + return true; + tree->temp_selection = node; + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + return true; + + } + + /* no item either means cancel selection on (select) click or a drag */ + if (!element) { + if ((pointer->buttons == (wimp_CLICK_SELECT << 4)) || + (pointer->buttons == (wimp_CLICK_SELECT << 8))) + tree_set_node_selected(tree, tree->root->child, false); + if ((pointer->buttons == (wimp_CLICK_SELECT << 4)) || + (pointer->buttons == (wimp_CLICK_ADJUST << 4))) { + + scroll.w = (wimp_w)tree->handle; + scroll.pause_zone_sizes.y0 = 80; + scroll.pause_zone_sizes.y1 = 80; + scroll.pause_duration = 0; + scroll.state_change = (void *)0; + error = xwimp_auto_scroll(wimp_AUTO_SCROLL_ENABLE_VERTICAL, + &scroll, 0); + if (error) + LOG(("xwimp_auto_scroll: 0x%x: %s", + error->errnum, error->errmess)); + + gui_current_drag_type = GUI_DRAG_TREE_SELECT; + ro_gui_tree_current_drag_tree = tree; + ro_gui_tree_current_drag_buttons = pointer->buttons; + + drag.w = (wimp_w)tree->handle; + drag.type = wimp_DRAG_USER_RUBBER; + drag.initial.x0 = pointer->pos.x; + drag.initial.x1 = pointer->pos.x; + drag.initial.y0 = pointer->pos.y; + drag.initial.y1 = pointer->pos.y; + drag.bbox.x0 = state.visible.x0; + drag.bbox.x1 = state.visible.x1; + drag.bbox.y0 = -16384;//state.visible.y0; + drag.bbox.y1 = 16384;//state.visible.y1 - tree->offset_y; + error = xwimp_drag_box_with_flags(&drag, + wimp_DRAG_BOX_KEEP_IN_LINE | + wimp_DRAG_BOX_CLIP); + if (error) + LOG(("xwimp_drag_box_with_flags: 0x%x: %s", + error->errnum, error->errmess)); + + } + return true; + } + + /* click on furniture or double click on folder toggles node expansion */ + if (((furniture) && ((pointer->buttons == wimp_CLICK_SELECT << 8) || + (pointer->buttons == wimp_CLICK_ADJUST << 8) || + (pointer->buttons == wimp_CLICK_SELECT) || + (pointer->buttons == wimp_CLICK_ADJUST))) || + ((!furniture) && (node->child) && + ((pointer->buttons == wimp_CLICK_SELECT) || + (pointer->buttons == wimp_CLICK_ADJUST)))) { + node->expanded = !node->expanded; + if (!furniture) + node->selected = false; + tree_handle_node_changed(tree, node, false, true); + return true; + } + + /* no use for any other furniture click */ + if (furniture) + return true; + + /* single/double alt+click starts editing */ + if ((node->editable) && (!tree->editing) && ((element->user_type == 0) || + (element->user_type == TREE_ELEMENT_URL)) && + ((pointer->buttons == wimp_CLICK_SELECT) || + (pointer->buttons == (wimp_CLICK_SELECT << 8)))) { + xosbyte1(osbyte_SCAN_KEYBOARD, 2 ^ 0x80, 0, &alt_pressed); + if ((alt_pressed == 0xff) && + (element->type != NODE_ELEMENT_SPRITE)) { + ro_gui_tree_start_edit(tree, element, pointer); + return true; + } + } + + /* double click starts launches the leaf */ + if ((pointer->buttons == wimp_CLICK_SELECT) || + (pointer->buttons == wimp_CLICK_ADJUST)) { + if (!ro_gui_tree_launch_node(node)) + return false; + if (pointer->buttons == wimp_CLICK_ADJUST) + ro_gui_tree_keypress(wimp_KEY_CONTROL + wimp_KEY_F2, tree); + return true; + } + + /* single click (select) cancels current selection and selects item */ + if (pointer->buttons == (wimp_CLICK_SELECT << 8)) { + if (!node->selected) { + tree_set_node_selected(tree, tree->root->child, false); + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + return true; + } + + /* single click (adjust) toggles item selection */ + if (pointer->buttons == (wimp_CLICK_ADJUST << 8)) { + node->selected = !node->selected; + tree_handle_node_element_changed(tree, &node->data); + return true; + } + + /* drag starts a drag operation */ + if ((!tree->editing) && ((pointer->buttons == (wimp_CLICK_SELECT << 4)) || + (pointer->buttons == (wimp_CLICK_ADJUST << 4)))) { + + if (!node->selected) { + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + + scroll.w = (wimp_w)tree->handle; + scroll.pause_zone_sizes.y0 = 80; + scroll.pause_zone_sizes.y1 = 80; + scroll.pause_duration = -1; + scroll.state_change = (void *)0; + error = xwimp_auto_scroll(wimp_AUTO_SCROLL_ENABLE_VERTICAL, + &scroll, 0); + if (error) + LOG(("xwimp_auto_scroll: 0x%x: %s", + error->errnum, error->errmess)); + + gui_current_drag_type = GUI_DRAG_TREE_MOVE; + ro_gui_tree_current_drag_tree = tree; + ro_gui_tree_current_drag_buttons = pointer->buttons; + + node = tree_get_selected_node(tree->root); + if (node) { + if (node->folder) { + if ((node->expanded) && + (ro_gui_wimp_sprite_exists("directoryo"))) + sprintf(ro_gui_tree_drag_name, "directoryo"); + else + sprintf(ro_gui_tree_drag_name, "directory"); + } else { + element = tree_find_element(node, TREE_ELEMENT_URL); + if (element) { + sprintf(ro_gui_tree_drag_name, "file_%.3x", + element->user_data); + } else { + sprintf(ro_gui_tree_drag_name, "file_xxx"); + } + if (!ro_gui_wimp_sprite_exists(ro_gui_tree_drag_name)) + sprintf(ro_gui_tree_drag_name, "file_xxx"); + } + } else { + sprintf(ro_gui_tree_drag_name, "package"); + } + + error = xdragasprite_start(dragasprite_HPOS_CENTRE | + dragasprite_VPOS_CENTRE | + dragasprite_BOUND_POINTER | + dragasprite_DROP_SHADOW, + (osspriteop_area *) 1, + ro_gui_tree_drag_name, &box, 0); + if (error) + LOG(("xdragasprite_start: 0x%x: %s", + error->errnum, error->errmess)); + return true; + } + + + return false; +} + + +/** + * Handles a menu closed event + * + * \param tree the tree to handle the event for + */ +void ro_gui_tree_menu_closed(struct tree *tree) { + assert(tree); + + if (tree->temp_selection) { + tree->temp_selection->selected = false; + tree_handle_node_element_changed(tree, &tree->temp_selection->data); + tree->temp_selection = NULL; + } +} + + +/** + * Starts an editing session + * + * \param tree the tree to start editing for + * \param element the element to edit + * \param pointer the pointer data to use for caret positioning (or NULL) + */ +void ro_gui_tree_start_edit(struct tree *tree, struct node_element *element, + wimp_pointer *pointer) { + os_error *error; + wimp_window_state state; + struct node *parent; + + assert(tree); + assert(element); + + if (tree->editing) + ro_gui_tree_stop_edit(tree); + + parent = element->parent; + if (&parent->data == element) + parent = parent->parent; + for (; parent; parent = parent->parent) { + if (!parent->expanded) { + parent->expanded = true; + tree_handle_node_changed(tree, parent, false, true); + } + } + + tree->editing = element; + snprintf(tree->edit_buffer, 256, element->text); + tree->edit_buffer[255] = '\0'; + ro_gui_tree_edit_icon.w = (wimp_w)tree->handle; + ro_gui_tree_edit_icon.icon.extent.x0 = tree->offset_x + element->box.x - 2; + ro_gui_tree_edit_icon.icon.extent.x1 = tree->offset_x + + element->box.x + element->box.width + 2; + ro_gui_tree_edit_icon.icon.extent.y1 = -tree->offset_y - element->box.y; + ro_gui_tree_edit_icon.icon.extent.y0 = -tree->offset_y - + element->box.y - element->box.height; + if (element->type == NODE_ELEMENT_TEXT_PLUS_SPRITE) + ro_gui_tree_edit_icon.icon.extent.x0 += NODE_INSTEP; + ro_gui_tree_edit_icon.icon.data.indirected_text.text = tree->edit_buffer; + error = xwimp_create_icon(&ro_gui_tree_edit_icon, + &(wimp_i)tree->edit_handle); + if (error) + LOG(("xwimp_create_icon: 0x%x: %s", + error->errnum, error->errmess)); + if (pointer) { + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + error = xwimp_set_caret_position((wimp_w)tree->handle, + (wimp_i)tree->edit_handle, + pointer->pos.x - state.visible.x0, 0, + element->box.height, -1); + } else { + error = xwimp_set_caret_position((wimp_w)tree->handle, + (wimp_i)tree->edit_handle, + 0, 0, -1, strlen(tree->edit_buffer)); + } + if (error) + LOG(("xwimp_set_caret_position: 0x%x: %s", + error->errnum, error->errmess)); + tree_handle_node_element_changed(tree, element); + ro_gui_tree_scroll_visible(tree, element); +} + + +/** + * Stops any current editing session + * + * \param tree the tree to stop editing for + */ +void ro_gui_tree_stop_edit(struct tree *tree) { + os_error *error; + + assert(tree); + + if (!tree->editing) return; + + error = xwimp_delete_icon((wimp_w)tree->handle, (wimp_i)tree->edit_handle); + if (error) + LOG(("xwimp_delete_icon: 0x%x: %s", + error->errnum, error->errmess)); + tree_handle_node_element_changed(tree, tree->editing); + tree->editing = NULL; + + error = xwimp_set_caret_position((wimp_w)tree->handle, -1, -100, + -100, 32, -1); + if (error) + LOG(("xwimp_set_caret_position: 0x%x: %s", + error->errnum, error->errmess)); + tree_recalculate_size(tree); +} + + +/** + * Scrolls the tree to make an element visible + * + * \param tree the tree to scroll + * \param element the element to display + */ +void ro_gui_tree_scroll_visible(struct tree *tree, struct node_element *element) { + wimp_window_state state; + int x0, x1, y0, y1; + os_error *error; + + assert(element); + + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + if (!(state.flags & wimp_WINDOW_OPEN)) + return; + x0 = state.xscroll; + y0 = -state.yscroll; + x1 = x0 + state.visible.x1 - state.visible.x0 - tree->offset_x; + y1 = y0 - state.visible.y0 + state.visible.y1 - tree->offset_y; + + state.yscroll = state.visible.y1 - state.visible.y0 - tree->offset_y - y1; + if ((element->box.y >= y0) && (element->box.y + element->box.height <= y1)) + return; + if (element->box.y < y0) + state.yscroll = -element->box.y; + if (element->box.y + element->box.height > y1) + state.yscroll = state.visible.y1 - state.visible.y0 - + tree->offset_y - + (element->box.y + element->box.height); + ro_gui_tree_open((wimp_open *)&state, tree); +} + + +/** + * Handles a window open request + * + * \param open the window state + * \param tree the tree to handle a request for + */ +void ro_gui_tree_open(wimp_open *open, struct tree *tree) { + os_error *error; + int width; + int height; + + width = open->visible.x1 - open->visible.x0; + if (width < (tree->offset_x + tree->width)) + width = tree->offset_x + tree->width; + height = open->visible.y1 - open->visible.y0; + if (height < (tree->offset_y + tree->height)) + height = tree->offset_y + tree->height; + + if ((height != tree->window_height) || (width != tree->window_width)) { + os_box extent = { 0, -height, width, 0}; + error = xwimp_set_extent((wimp_w)tree->handle, &extent); + if (error) { + LOG(("xwimp_set_extent: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + tree->window_width = width; + tree->window_height = height; + } + + error = xwimp_open_window(open); + if (error) { + LOG(("xwimp_open_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } +} + + +/** + * Handles a keypress for a tree + * + * \param key the key pressed + * \param tree the tree to handle a keypress for + * \return whether the key was processed + */ +bool ro_gui_tree_keypress(int key, struct tree *tree) { + os_error *error; + char *new_string; + + /* Handle basic keys + */ + switch (key) { + case 1: /* CTRL+A */ + ro_gui_tree_stop_edit(tree); + if (tree->root->child) { + tree->temp_selection = NULL; + tree_set_node_selected(tree, tree->root, true); + } + return true; + case 24: /* CTRL+X */ + ro_gui_tree_stop_edit(tree); + tree_delete_selected_nodes(hotlist_tree, hotlist_tree->root); + return true; + case 26: /* CTRL+Z */ + tree->temp_selection = NULL; + ro_gui_tree_stop_edit(tree); + tree_set_node_selected(tree, tree->root, false); + return true; + case wimp_KEY_RETURN: + if (tree->editing) { + new_string = strdup(tree->edit_buffer); + if (new_string) { + if (tree->editing->text) { + free(tree->editing->text); + tree->editing->text = NULL; + } + tree->editing->text = new_string; + } + ro_gui_tree_stop_edit(tree); + tree_recalculate_size(tree); + } else { + ro_gui_tree_launch_selected(tree); + } + return true; + case wimp_KEY_CONTROL + wimp_KEY_F2: + error = xwimp_close_window((wimp_w)tree->handle); + if (error) + LOG(("xwimp_close_window: 0x%x: %s", + error->errnum, error->errmess)); + return true; + case wimp_KEY_ESCAPE: + if (tree->editing) { + ro_gui_tree_stop_edit(tree); + } else { + /* \todo cancel drags etc. */ + } + } + return false; +} + + +/** + * Handles the completion of a selection drag (GUI_DRAG_TREE_SELECT) + * + * \param drag the drag box information + */ +void ro_gui_tree_selection_drag_end(wimp_dragged *drag) { + wimp_window_state state; + wimp_auto_scroll_info scroll; + os_error *error; + int x0, y0, x1, y1; + + scroll.w = (wimp_w)ro_gui_tree_current_drag_tree->handle; + error = xwimp_auto_scroll(0, &scroll, 0); + if (error) + LOG(("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess)); + + state.w = (wimp_w)ro_gui_tree_current_drag_tree->handle; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + + x0 = drag->final.x0 - state.visible.x0 - state.xscroll + + ro_gui_tree_current_drag_tree->offset_x; + y0 = state.visible.y1 - state.yscroll - drag->final.y0 - + ro_gui_tree_current_drag_tree->offset_y; + x1 = drag->final.x1 - state.visible.x0 - state.xscroll + + ro_gui_tree_current_drag_tree->offset_x; + y1 = state.visible.y1 - state.yscroll - drag->final.y1 - + ro_gui_tree_current_drag_tree->offset_y; + tree_handle_selection_area(ro_gui_tree_current_drag_tree, x0, y0, + x1 - x0, y1 - y0, + (ro_gui_tree_current_drag_buttons == (wimp_CLICK_ADJUST << 4))); + + /* send an empty keypress to stimulate the tree owner to update the GUI. + for this to work, we must always own the caret when this function is + called. */ + error = xwimp_process_key(0); + if (error) + LOG(("xwimp_process_key: 0x%x: %s", + error->errnum, error->errmess)); +} + + +/** + * Converts screen co-ordinates to tree ones + * + * \param tree the tree to calculate for + * \param x the screen x co-ordinate + * \param x the screen y co-ordinate + * \param tree_x updated to the tree x co-ordinate + * \param tree_y updated to the tree y co-ordinate + */ +void ro_gui_tree_get_tree_coordinates(struct tree *tree, int x, int y, + int *tree_x, int *tree_y) { + wimp_window_state state; + os_error *error; + + state.w = (wimp_w)tree->handle; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + *tree_x = x - state.visible.x0 - state.xscroll + tree->offset_x; + *tree_y = state.visible.y1 - state.yscroll - y - tree->offset_y; +} + + +/** + * Handles the completion of a move drag (GUI_DRAG_TREE_MOVE) + * + * \param drag the drag box information + */ +void ro_gui_tree_move_drag_end(wimp_dragged *drag) { + wimp_pointer pointer; + wimp_auto_scroll_info scroll; + os_error *error; + struct node *node; + bool before; + int x, y; + + scroll.w = (wimp_w)ro_gui_tree_current_drag_tree->handle; + error = xwimp_auto_scroll(0, &scroll, 0); + if (error) + LOG(("xwimp_auto_scroll: 0x%x: %s", error->errnum, error->errmess)); + + error = xwimp_get_pointer_info(&pointer); + if (error) { + LOG(("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + + /* todo: handle export */ + if (pointer.w != (wimp_w)ro_gui_tree_current_drag_tree->handle) + return; + + /* internal drag */ + ro_gui_tree_get_tree_coordinates(ro_gui_tree_current_drag_tree, + drag->final.x0 + 34, drag->final.y0 + 34, &x, &y); + node = tree_get_link_details(ro_gui_tree_current_drag_tree, x, y, &before); + tree_move_selected_nodes(ro_gui_tree_current_drag_tree, node, before); +} + + +/** + * Launches all selected nodes. + * + * \param tree the tree to launch all selected nodes for + */ +void ro_gui_tree_launch_selected(struct tree *tree) { + assert(tree); + + if (tree->root->child) + ro_gui_tree_launch_selected_node(tree->root->child, false); +} + + +/** + * Launches all selected nodes. + * + * \param node the node to launch all selected nodes for + */ +void ro_gui_tree_launch_selected_node(struct node *node, bool all) { + for (; node; node = node->next) { + if (((node->selected) || (all)) && (!node->folder)) + ro_gui_tree_launch_node(node); + if ((node->child) && ((node->expanded) || (node->selected) | (all))) + ro_gui_tree_launch_selected_node(node->child, + (node->selected) | (all)); + } +} + + +/** + * Launches a node using all known methods. + * + * \param node the node to launch + * \return whether the node could be launched + */ +bool ro_gui_tree_launch_node(struct node *node) { + struct node_element *element; + + assert(node); + + element = tree_find_element(node, TREE_ELEMENT_URL); + if (element) { + browser_window_create(element->text, NULL, 0); + return true; + } + + return false; +} diff --git a/riscos/treeview.h b/riscos/treeview.h new file mode 100644 index 000000000..1757d2fd8 --- /dev/null +++ b/riscos/treeview.h @@ -0,0 +1,45 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + */ + +/** \file + * Generic tree handling (interface). + */ + +#ifndef _NETSURF_RISCOS_TREEVIEW_H_ +#define _NETSURF_RISCOS_TREEVIEW_H_ + +#include <stdbool.h> +#include "oslib/osspriteop.h" +#include "oslib/wimp.h" +#include "netsurf/desktop/tree.h" + +#define TREE_TEXT_HEIGHT 40 +#define TREE_SPRITE_WIDTH 40 /* text plus sprite entries only */ + +struct node_sprite { + osspriteop_area *area; + char name[12]; + char expanded_name[12]; +}; + +bool ro_gui_tree_initialise(void); +void ro_gui_tree_redraw(wimp_draw *redraw, struct tree *tree); +bool ro_gui_tree_click(wimp_pointer *pointer, struct tree *tree); +void ro_gui_tree_menu_closed(struct tree *tree); +void ro_gui_tree_stop_edit(struct tree *tree); +void ro_gui_tree_open(wimp_open *open, struct tree *tree); +bool ro_gui_tree_keypress(int key, struct tree *tree); +void ro_gui_tree_selection_drag_end(wimp_dragged *drag); +void ro_gui_tree_move_drag_end(wimp_dragged *drag); +void ro_gui_tree_launch_selected(struct tree *tree); +void ro_gui_tree_start_edit(struct tree *tree, struct node_element *element, + wimp_pointer *pointer); +void ro_gui_tree_scroll_visible(struct tree *tree, struct node_element *element); +void ro_gui_tree_get_tree_coordinates(struct tree *tree, int x, int y, + int *tree_x, int *tree_y); + +#endif diff --git a/riscos/wimp.c b/riscos/wimp.c index 160b3da0d..012cdd6fb 100644 --- a/riscos/wimp.c +++ b/riscos/wimp.c @@ -311,6 +311,16 @@ void ro_gui_set_window_title(wimp_w w, const char *text) { strncpy(window.title_data.indirected_text.text, text, (unsigned int)window.title_data.indirected_text.size - 1); window.title_data.indirected_text.text[window.title_data.indirected_text.size - 1] = '\0'; + + /* Redraw accordingly + */ + error = xwimp_force_redraw_title(w); + if (error) { + LOG(("xwimp_force_redraw_title: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } } @@ -430,7 +440,7 @@ void ro_gui_open_window_centre(wimp_w parent, wimp_w child) { /* Move to the centre of the parent at the top of the stack */ dimension = state.visible.x1 - state.visible.x0; - scroll_width = ro_get_vscroll_width(hotlist_window); + scroll_width = ro_get_vscroll_width(history_window); state.visible.x0 = mid_x - (dimension + scroll_width) / 2; state.visible.x1 = state.visible.x0 + dimension; dimension = state.visible.y1 - state.visible.y0; diff --git a/riscos/wimp.h b/riscos/wimp.h index 26f44bd74..744917efb 100644 --- a/riscos/wimp.h +++ b/riscos/wimp.h @@ -53,4 +53,5 @@ void ro_gui_open_pane(wimp_w parent, wimp_w pane, int offset); wimp_w ro_gui_set_window_background_colour(wimp_w window, wimp_colour background); void ro_gui_set_icon_colours(wimp_w window, wimp_i icon, wimp_colour foreground, wimp_colour background); + #endif diff --git a/riscos/window.c b/riscos/window.c index 42062718b..b9ba998c0 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -16,6 +16,7 @@ #include <assert.h> #include <math.h> #include <stdbool.h> +#include <time.h> #include <string.h> #include "oslib/colourtrans.h" #include "oslib/osspriteop.h" @@ -32,10 +33,12 @@ #include "netsurf/riscos/options.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/thumbnail.h" +#include "netsurf/riscos/treeview.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/url.h" #include "netsurf/utils/utils.h" +#include "netsurf/utils/messages.h" /** List of all browser windows. */ @@ -537,7 +540,7 @@ void gui_window_update_box(struct gui_window *g, /* Set the current redraw gui_window to get options from */ ro_gui_current_redraw_gui = g; -/* if (data->redraw.full_redraw) */ +/* if (data->redraw.full_redraw) */ use_buffer = use_buffer || g->option.buffer_animations; plot = ro_plotters; @@ -807,10 +810,10 @@ char *gui_window_get_url(struct gui_window *g) /** * Forces all windows to be set to the current theme * - * /param g the gui window to update + * /param g the gui window to update */ void ro_gui_window_update_theme(void) { - int height; + int height; struct gui_window *g; for (g = window_list; g; g = g->next) { if (g->toolbar) { @@ -821,7 +824,7 @@ void ro_gui_window_update_theme(void) { if (height != 0) ro_gui_window_update_dimensions(g, height); } else { - if (height != g->toolbar->height) + if (height != g->toolbar->height) ro_gui_window_update_dimensions(g, height - g->toolbar->height); } @@ -833,21 +836,25 @@ void ro_gui_window_update_theme(void) { ro_gui_theme_destroy_toolbar(hotlist_toolbar); hotlist_toolbar = NULL; } - ro_gui_theme_attach_toolbar(hotlist_toolbar, hotlist_window); - xwimp_force_redraw(hotlist_window, 0, -16384, 16384, 16384); + if (hotlist_tree) { + ro_gui_theme_attach_toolbar(hotlist_toolbar, + (wimp_w)hotlist_tree->handle); + hotlist_tree->offset_y = hotlist_toolbar->height; + xwimp_force_redraw((wimp_w)hotlist_tree->handle, + 0, -16384, 16384, 16384); + } } - } /** * Forces the windows extent to be updated * - * /param g the gui window to update + * /param g the gui window to update * /param yscroll an amount to scroll the vertical scroll bar by */ void ro_gui_window_update_dimensions(struct gui_window *g, int yscroll) { - os_error *error; + os_error *error; wimp_window_state state; if (!g) return; state.w = g->window; @@ -1089,6 +1096,7 @@ void ro_gui_window_mouse_at(struct gui_window *g, wimp_pointer *pointer) void ro_gui_toolbar_click(struct gui_window *g, wimp_pointer *pointer) { + struct node *node; char url[80]; /* Store the toolbar @@ -1160,11 +1168,17 @@ void ro_gui_toolbar_click(struct gui_window *g, wimp_pointer *pointer) break; case ICON_TOOLBAR_BOOKMARK: - if (pointer->buttons == wimp_CLICK_ADJUST) { - if (g->bw->current_content) - ro_gui_hotlist_add(g->title, - g->bw->current_content); - } else { + if ((pointer->buttons == wimp_CLICK_ADJUST) && (hotlist_tree)) { + node = tree_create_URL_node(hotlist_tree->root, + messages_get(g->title), + g->bw->current_content->url, + ro_content_filetype(g->bw->current_content), + time(NULL), -1, 0); + tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, 0, + NODE_INSTEP, 16384); + tree_handle_node_changed(hotlist_tree, node, false, true); + ro_gui_tree_scroll_visible(hotlist_tree, &node->data); + } else if (hotlist_tree) { ro_gui_hotlist_show(); } break; @@ -1790,7 +1804,6 @@ void ro_gui_window_clone_options(struct browser_window *new_bw, */ if (!old_gui) { new_gui->option.scale = ((float)option_scale) / 100; - new_gui->option.animate_images = option_animate_images; new_gui->option.background_images = option_background_images; new_gui->option.background_blending = option_background_blending; new_gui->option.buffer_animations = option_buffer_animations; @@ -1840,7 +1853,6 @@ void ro_gui_window_default_options(struct browser_window *bw) { /* Save the basic options */ option_scale = gui->option.scale * 100; - option_animate_images = gui->option.animate_images; option_background_blending = gui->option.background_blending; option_buffer_animations = gui->option.buffer_animations; option_buffer_everything = gui->option.buffer_everything; |