diff options
109 files changed, 11096 insertions, 7531 deletions
diff --git a/!NetSurf/Resources/Icons/content.png b/!NetSurf/Resources/Icons/content.png Binary files differnew file mode 100644 index 000000000..53234c38c --- /dev/null +++ b/!NetSurf/Resources/Icons/content.png diff --git a/!NetSurf/Resources/Icons/directory.png b/!NetSurf/Resources/Icons/directory.png Binary files differnew file mode 100644 index 000000000..de3d7687f --- /dev/null +++ b/!NetSurf/Resources/Icons/directory.png diff --git a/!NetSurf/Resources/de/Messages b/!NetSurf/Resources/de/Messages index 175f20008..0349c6f7c 100644 --- a/!NetSurf/Resources/de/Messages +++ b/!NetSurf/Resources/de/Messages @@ -189,6 +189,8 @@ ResetUsage:Statistik zurücksetzen New:Neu Expand:Expandieren Collapse:Zusammenfalten +Tree:Tree +TreeExport:Export... # New hotlist entry menu # @@ -200,6 +202,7 @@ Link:Eintrag All:Alles Folders:Verzeichnisse Links:Einträge +Domains:Domains # Other menus # @@ -373,6 +376,13 @@ DateYesterday:Gestern Date1Week:vorige Woche Date2Week:vor 2 Wochen Date3Week:vor 3 Wochen +Sunday:Sunday +Monday:Monday +Tuesday:Tuesday +Wednesday:Wednesday +Thursday:Thursday +Friday:Friday +Saturday:Saturday # Download user interface tokens diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages index 6d62ac3cd..84ea3f69f 100644 --- a/!NetSurf/Resources/en/Messages +++ b/!NetSurf/Resources/en/Messages @@ -189,6 +189,8 @@ ResetUsage:Reset statistics New:New Expand:Expand Collapse:Collapse +Tree:Tree +TreeExport:Export... # New hotlist entry menu # @@ -200,6 +202,7 @@ Link:Address All:All Folders:Directories Links:Addresses +Domains:Domains # Other menus # @@ -332,6 +335,7 @@ TreeNewFolder:New directory TreeLaunch:Visit URL TreeDelete:Delete + # Tree export # TreeHotlist:NetSurf hotlist @@ -374,6 +378,13 @@ DateYesterday:Yesterday Date1Week:Last week Date2Week:2 weeks ago Date3Week:3 weeks ago +Sunday:Sunday +Monday:Monday +Tuesday:Tuesday +Wednesday:Wednesday +Thursday:Thursday +Friday:Friday +Saturday:Saturday # Download user interface tokens @@ -534,6 +545,8 @@ gtkGlobalHistoryAccel:<ctrl><shift>h gtkAddBookMarks:_Add to Bookmarks… gtkShowBookMarks:_Show Bookmarks… gtkShowBookMarksAccel:F6 +gtkShowCookies:Show _Cookies… +gtkShowCookiesAccel:F9 gtkOpenLocation:_Open Location… gtkOpenLocationAccel:<ctrl>l @@ -607,15 +620,15 @@ Cancel:Cancel # SSLCerts:SSL certificates SSLError:NetSurf failed to verify the authenticity of an SSL certificate. Please verify the details presented below. -Subject:Subject -Issuer:Issuer -Version:Version -ValidFrom:Valid from -ValidTo:Valid until -Type:Type -Serial:Serial -Accept:Accept -Reject:Reject +SSL_Certificate_Subject:Subject: %s +SSL_Certificate_Issuer:Issuer: %s +SSL_Certificate_Version:Version: %ld +SSL_Certificate_ValidFrom:Valid from: %s +SSL_Certificate_ValidTo:Valid until: %s +SSL_Certificate_Type:Type: %i +SSL_Certificate_Serial:Serial: %ld +SSL_Certificate_Accept:Accept +SSL_Certificate_Reject:Reject # Content @@ -723,7 +736,7 @@ Resolvers:No domain name servers are configured, so only browsing local files wi NoDiscSpace:Not enough space available on disc. Template:A window template is missing from the Templates file. Please reinstall NetSurf. HotlistSaveError:The hotlist was unable to be correctly saved. -HotlistLoadError:The hotlist was unable to be correctly loaded. +TreeLoadError:The tree was unable to be correctly loaded. NoDirError:%s is not a directory NoPathError:To save, drag the icon to a directory display NoNameError:Please enter a name diff --git a/!NetSurf/Resources/fr/Messages b/!NetSurf/Resources/fr/Messages index bfe563d90..c16d0f09a 100644 --- a/!NetSurf/Resources/fr/Messages +++ b/!NetSurf/Resources/fr/Messages @@ -189,6 +189,8 @@ ResetUsage:RAZ des statistiques New:Nouveau Expand:Déployer Collapse:Regrouper +Tree:Tree +TreeExport:Export... # New hotlist entry menu # @@ -200,6 +202,7 @@ Link:Adresse All:Tout Folders:Dossiers Links:Adresses +Domains:Domains # Other menus # @@ -373,6 +376,13 @@ DateYesterday:Hier Date1Week:La semaine dernière Date2Week:Il y a 2 semaines Date3Week:Il y a 3 semaines +Sunday:Sunday +Monday:Monday +Tuesday:Tuesday +Wednesday:Wednesday +Thursday:Thursday +Friday:Friday +Saturday:Saturday # Download user interface tokens diff --git a/!NetSurf/Resources/it/Messages b/!NetSurf/Resources/it/Messages index e79ce93a5..7d4e834de 100755 --- a/!NetSurf/Resources/it/Messages +++ b/!NetSurf/Resources/it/Messages @@ -191,6 +191,8 @@ ResetUsage:Resetta statistiche New:Nuovo Expand:Espandi Collapse:Raggruppa +Tree:Tree +TreeExport:Export... # New hotlist entry menu # @@ -202,6 +204,7 @@ Link:Indirizzo All:Tutte Folders:Cartelle Links:Indirizzi +Domains:Domains # Other menus # @@ -375,6 +378,13 @@ DateYesterday:Ieri Date1Week:Ultima settimana Date2Week:2 settimane fa Date3Week:3 settimane fa +Sunday:Sunday +Monday:Monday +Tuesday:Tuesday +Wednesday:Wednesday +Thursday:Thursday +Friday:Friday +Saturday:Saturday # Download user interface tokens diff --git a/!NetSurf/Resources/nl/Messages b/!NetSurf/Resources/nl/Messages index 2c1b3fa9b..f837f7dbd 100644 --- a/!NetSurf/Resources/nl/Messages +++ b/!NetSurf/Resources/nl/Messages @@ -189,6 +189,8 @@ ResetUsage:Statistiek op nul zetten New:Nieuw Expand:Uitklappen Collapse:Inklappen +Tree:Tree +TreeExport:Export... # New hotlist entry menu # @@ -200,6 +202,7 @@ Link:Adres All:Alles Folders:Mappen Links:Adressen +Domains:Domains # Other menus # @@ -373,6 +376,13 @@ DateYesterday:gisteren Date1Week:vorige week Date2Week:2 weken geleden Date3Week:3 weken geleden +Sunday:Sunday +Monday:Monday +Tuesday:Tuesday +Wednesday:Wednesday +Thursday:Thursday +Friday:Friday +Saturday:Saturday # Download user interface tokens diff --git a/Makefile.sources b/Makefile.sources index 57166df51..2239cc7cd 100644 --- a/Makefile.sources +++ b/Makefile.sources @@ -13,8 +13,9 @@ S_RENDER := box.c box_construct.c box_normalise.c favicon.c \ hubbub_binding.c imagemap.c layout.c list.c table.c textplain.c S_UTILS := base64.c filename.c hashtable.c http.c locale.c messages.c \ talloc.c url.c utf8.c utils.c useragent.c findresource.c -S_DESKTOP := knockout.c options.c plot_style.c print.c search.c \ - searchweb.c scroll.c textarea.c tree.c version.c +S_DESKTOP := cookies.c history_global_core.c hotlist.c knockout.c \ + options.c plot_style.c print.c search.c searchweb.c scroll.c \ + sslcert.c textarea.c tree.c tree_url_node.c version.c \ # S_COMMON are sources common to all builds S_COMMON := $(addprefix content/,$(S_CONTENT)) \ @@ -47,7 +48,7 @@ S_RISCOS := 401login.c artworks.c assert.c awrender.s bitmap.c buffer.c \ schedule.c search.c searchweb.c sprite.c sslcert.c \ textarea.c textselection.c theme.c theme_install.c thumbnail.c \ treeview.c ucstables.c uri.c url_complete.c url_protocol.c \ - wimp.c wimp_event.c window.c gui/progress_bar.c \ + url_suggest.c wimp.c wimp_event.c window.c gui/progress_bar.c \ gui/status_bar.c \ $(addprefix configure/,con_cache.c con_connect.c con_content.c \ con_fonts.c con_home.c con_image.c con_inter.c con_language.c \ @@ -62,22 +63,22 @@ S_GTK := font_pango.c gtk_bitmap.c gtk_gui.c gtk_schedule.c \ gtk_completion.c gtk_login.c gtk_throbber.c gtk_selection.c \ gtk_history.c gtk_window.c gtk_filetype.c gtk_download.c \ gtk_menu.c gtk_print.c gtk_save.c gtk_search.c gtk_tabs.c \ - gtk_theme.c gtk_toolbar.c sexy_icon_entry.c gtk_compat.c \ + gtk_theme.c gtk_toolbar.c sexy_icon_entry.c gtk_compat.c \ + gtk_cookies.c gtk_hotlist.c \ $(addprefix dialogs/,gtk_options.c gtk_about.c gtk_source.c) S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c) # code in utils/container.ch is non-universal it seems # S_WINDOWS are sources purely for the windows build -S_WINDOWS := about.c bitmap.c download.c filetype.c findfile.c font.c \ - gui.c history.c hotlist.c localhistory.c login.c misc.c plot.c \ - prefs.c schedule.c thumbnail.c tree.c +S_WINDOWS := about.c bitmap.c download.c filetype.c findfile.c font.c \ + gui.c localhistory.c login.c misc.c plot.c prefs.c schedule.c \ + thumbnail.c tree.c S_WINDOWS := $(addprefix windows/,$(S_WINDOWS)) # S_BEOS are sources purely for the BeOS build S_BEOS := beos_about.cpp beos_bitmap.cpp beos_fetch_rsrc.cpp \ - beos_filetype.cpp beos_font.cpp beos_gui.cpp beos_history.cpp \ - beos_login.cpp beos_options.cpp beos_plotters.cpp \ - beos_save_complete.cpp \ + beos_filetype.cpp beos_font.cpp beos_gui.cpp beos_login.cpp \ + beos_options.cpp beos_plotters.cpp beos_save_complete.cpp \ beos_scaffolding.cpp beos_search.cpp beos_schedule.cpp \ beos_thumbnail.cpp beos_treeview.cpp beos_throbber.cpp \ beos_window.cpp @@ -102,9 +103,9 @@ S_AMIGA := $(addprefix amiga/,$(S_AMIGA)) # S_FRAMEBUFFER are sources purely for the framebuffer build -S_FRAMEBUFFER := gui.c framebuffer.c tree.c history.c hotlist.c \ - save.c schedule.c thumbnail.c misc.c bitmap.c filetype.c \ - login.c findfile.c localhistory.c +S_FRAMEBUFFER := gui.c framebuffer.c tree.c save.c schedule.c \ + thumbnail.c misc.c bitmap.c filetype.c login.c findfile.c \ + localhistory.c S_FRAMEBUFFER_FBTK := fbtk.c event.c fill.c bitmap.c user.c window.c \ text.c scroll.c osk.c diff --git a/amiga/cookies.c b/amiga/cookies.c index cffa52337..76e2173f2 100755 --- a/amiga/cookies.c +++ b/amiga/cookies.c @@ -1,6 +1,5 @@ /* - * Copyright 2006 Richard Wilson <info@tinct.net> - * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,151 +16,23 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/** \file - * Cookies (implementation). - */ - -#include <string.h> -#include "content/urldb.h" -#include "desktop/cookies.h" -#include "desktop/tree.h" -#include "utils/messages.h" -#include "utils/log.h" -#include "amiga/cookies.h" #include <proto/exec.h> -#include <assert.h> -#include "utils/utils.h" - -static bool cookies_init; - -struct node *ami_cookies_find(const char *url); +#include "amiga/cookies.h" +#include "desktop/cookies.h" +#include "amiga/tree.h" -/** - * Initialise cookies tree - */ void ami_cookies_initialise(void) { - if(cookies_tree) return; - - /* Create an empty tree */ - cookies_tree = AllocVec(sizeof(struct tree),MEMF_CLEAR | MEMF_PRIVATE); - if (!cookies_tree) { - warn_user("NoMemory", 0); - return; - } - cookies_tree->root = tree_create_folder_node(NULL, "Root"); - if (!cookies_tree->root) { - warn_user("NoMemory", 0); - FreeVec(cookies_tree); - cookies_tree = NULL; - } - cookies_tree->root->expanded = true; - cookies_tree->movable = false; - cookies_tree->no_drag = true; - - cookies_init = true; - urldb_iterate_cookies(cookies_update); - cookies_init = false; - tree_initialise(cookies_tree); -} - -/** - * Perform cookie addition - * - * \param data Cookie data for a domain, or NULL - * \return true (for urldb_iterate_entries) - */ -bool cookies_update(const char *domain, const struct cookie_data *data) -{ - struct node *parent; - struct node *node = NULL; - struct node *child; - struct node *add; - const struct cookie_data *cookie = NULL; - bool expanded; - - assert(domain); - - /* check if we're a domain, and add get the first cookie */ - if (data) - for (cookie = data; cookie->prev; cookie = cookie->prev); - - if (!cookies_init) { - node = ami_cookies_find(domain); - if (node) { - /* mark as deleted so we don't remove the cookies */ - expanded = node->expanded; - for (child = node->child; child; child = child->next) - child->deleted = true; - if (node->child) - tree_delete_node(cookies_tree, node->child, - true); - /* deleting will have contracted our node */ - node->expanded = expanded; - } - if (!data) { - if (!node) - return true; - tree_delete_node(cookies_tree, node, false); - tree_handle_node_changed(cookies_tree, - cookies_tree->root, true, false); - return true; - } - } - - if (!node) { - for (parent = cookies_tree->root->child; parent; - parent = parent->next) { - if (strcmp(domain, parent->data.text) == 0) - break; - } - if (!parent) { - node = tree_create_folder_node(cookies_tree->root, - domain); - } else { - node = parent; - } - } - if (!node) - return true; - node->editable = false; + cookies_window = ami_tree_create(cookies_get_tree_flags(), NULL); - for (; cookie; cookie = cookie->next) { - add = tree_create_cookie_node(node, cookie); - if (add && !cookies_init) - tree_handle_node_changed(cookies_tree, add, - true, false); - } - if (!cookies_init) { - tree_handle_node_changed(cookies_tree, node, - true, false); -/* - tree_redraw_area(cookies_tree, - node->box.x - NODE_INSTEP, - 0, NODE_INSTEP, 16384); -*/ - } - return true; -} - -/** - * Find an entry in the cookie tree - * - * \param url The URL to find - * \return Pointer to node, or NULL if not found - */ -struct node *ami_cookies_find(const char *url) -{ - struct node *node; + if(!cookies_window) return; - for (node = cookies_tree->root->child; node; node = node->next) { - if (!strcmp(url, node->data.text)) - return node; - } - return NULL; + cookies_initialise(ami_tree_get_tree(cookies_window)); } void ami_cookies_free() { - FreeVec(cookies_tree); + cookies_cleanup(); + ami_tree_destroy(cookies_window); + cookies_window = NULL; } diff --git a/amiga/cookies.h b/amiga/cookies.h index 4d376434c..d6922750c 100755 --- a/amiga/cookies.h +++ b/amiga/cookies.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -19,9 +19,10 @@ #ifndef AMIGA_COOKIES_H #define AMIGA_COOKIES_H #include "desktop/tree.h" +#include "amiga/tree.h" void ami_cookies_initialise(void); void ami_cookies_free(void); -struct tree *cookies_tree; +struct treeview_window *cookies_window; #endif diff --git a/amiga/gui.c b/amiga/gui.c index 6372a435a..759b4f932 100755 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -143,6 +143,8 @@ BOOL screen_closed = FALSE; struct MsgPort *applibport = NULL; ULONG applibsig = 0; +const char tree_directory_icon_name[100]; +const char tree_content_icon_name[100]; extern colour scroll_widget_fg_colour; extern colour scroll_widget_bg_colour; extern colour scroll_widget_arrow_colour; @@ -324,9 +326,6 @@ void ami_set_options(void) if((!option_url_file) || (option_url_file[0] == '\0')) option_url_file = (char *)strdup("PROGDIR:Resources/URLs"); - if((!option_recent_file) || (option_recent_file[0] == '\0')) - option_recent_file = (char *)strdup("PROGDIR:Resources/Recent"); - /* if((!option_cookie_jar) || (option_cookie_jar[0] == '\0')) option_cookie_jar = (char *)strdup("PROGDIR:Resources/CookieJar"); @@ -453,7 +452,6 @@ void gui_init(int argc, char** argv) plot=amiplot; - ami_init_menulabs(); if(option_context_menu) ami_context_menu_init(); schedule_list = NewObjList(); @@ -462,15 +460,6 @@ void gui_init(int argc, char** argv) urldb_load(option_url_file); urldb_load_cookies(option_cookie_file); - if(lock = Lock(option_hotlist_file,SHARED_LOCK)) - { - UnLock(lock); - hotlist = options_load_tree(option_hotlist_file); - } - - if(!hotlist) ami_hotlist_init(&hotlist); - ami_global_history_initialise(); - ami_cookies_initialise(); save_complete_init(); ami_theme_init(); ami_init_mouse_pointers(); @@ -567,6 +556,18 @@ static void gui_init2(int argc, char** argv) notalreadyrunning = ami_arexx_init(); + /* Treeview init code ends up calling a font function which needs this */ + browserglob.scale = 1.0; + glob = &browserglob; + /**/ + + ami_get_theme_filename(&tree_directory_icon_name,"theme_list_folder",true); + ami_get_theme_filename(&tree_content_icon_name,"theme_list_content",true); + ami_hotlist_initialise(option_hotlist_file); + ami_cookies_initialise(); + ami_global_history_initialise(); + sslcert_init(); + search_web_provider_details(option_search_provider); if(argc) // argc==0 is started from wb @@ -1331,7 +1332,7 @@ void ami_handle_msg(void) case 'h': if((option_kiosk_mode == false) && (gwin->bw->browser_window_type == BROWSER_WINDOW_NORMAL)) - ami_open_tree(hotlist,AMI_TREE_HOTLIST); + ami_tree_open(hotlist_window, AMI_TREE_HOTLIST); break; /* The following aren't available from the menu at the moment */ @@ -2002,11 +2003,10 @@ void gui_quit(void) urldb_save(option_url_file); urldb_save_cookies(option_cookie_file); - options_save_tree(hotlist,option_hotlist_file,messages_get("TreeHotlist")); - void ami_global_history_save(); - + ami_hotlist_free(option_hotlist_file); ami_cookies_free(); ami_global_history_free(); + sslcert_cleanup(); hubbub_finalise(ns_realloc,NULL); @@ -2033,7 +2033,10 @@ void gui_quit(void) FreeVec(nsscreentitle); if(option_context_menu) ami_context_menu_free(); - ami_free_menulabs(); + +/* fixme: need newmenu struct propd to this function - should this be freed here? + ami_free_menulabs(menu); +*/ ami_mouse_pointers_free(); ami_clipboard_free(); @@ -2187,7 +2190,6 @@ void ami_toggletabbar(struct gui_window_2 *gwin, bool show) struct gui_window *gui_create_browser_window(struct browser_window *bw, struct browser_window *clone, bool new_tab) { - struct NewMenu *menu; struct gui_window *gwin = NULL; bool closegadg=TRUE; struct Node *node; @@ -2357,7 +2359,7 @@ struct gui_window *gui_create_browser_window(struct browser_window *bw, { ULONG addtabclosegadget = TAG_IGNORE; - menu = ami_create_menu(bw->browser_window_type); + ami_create_menu(bw->browser_window_type, gwin->shared); NewList(&gwin->shared->tab_list); gwin->tab_node = AllocClickTabNode(TNA_Text,messages_get("NetSurf"), @@ -2389,28 +2391,28 @@ struct gui_window *gui_create_browser_window(struct browser_window *bw, gwin->shared->helphints[GID_ADDTAB] = remove_escape_chars(messages_get("HelpAddTab"), true); - ami_get_theme_filename(nav_west,"theme_nav_west"); - ami_get_theme_filename(nav_west_s,"theme_nav_west_s"); - ami_get_theme_filename(nav_west_g,"theme_nav_west_g"); - ami_get_theme_filename(nav_east,"theme_nav_east"); - ami_get_theme_filename(nav_east_s,"theme_nav_east_s"); - ami_get_theme_filename(nav_east_g,"theme_nav_east_g"); - ami_get_theme_filename(stop,"theme_stop"); - ami_get_theme_filename(stop_s,"theme_stop_s"); - ami_get_theme_filename(stop_g,"theme_stop_g"); - ami_get_theme_filename(reload,"theme_reload"); - ami_get_theme_filename(reload_s,"theme_reload_s"); - ami_get_theme_filename(reload_g,"theme_reload_g"); - ami_get_theme_filename(home,"theme_home"); - ami_get_theme_filename(home_s,"theme_home_s"); - ami_get_theme_filename(home_g,"theme_home_g"); - ami_get_theme_filename(closetab,"theme_closetab"); - ami_get_theme_filename(closetab_s,"theme_closetab_s"); - ami_get_theme_filename(closetab_g,"theme_closetab_g"); - ami_get_theme_filename(addtab,"theme_addtab"); - ami_get_theme_filename(addtab_s,"theme_addtab_s"); - ami_get_theme_filename(addtab_g,"theme_addtab_g"); - ami_get_theme_filename(tabthrobber,"theme_tab_loading"); + ami_get_theme_filename(nav_west,"theme_nav_west",false); + ami_get_theme_filename(nav_west_s,"theme_nav_west_s",false); + ami_get_theme_filename(nav_west_g,"theme_nav_west_g",false); + ami_get_theme_filename(nav_east,"theme_nav_east",false); + ami_get_theme_filename(nav_east_s,"theme_nav_east_s",false); + ami_get_theme_filename(nav_east_g,"theme_nav_east_g",false); + ami_get_theme_filename(stop,"theme_stop",false); + ami_get_theme_filename(stop_s,"theme_stop_s",false); + ami_get_theme_filename(stop_g,"theme_stop_g",false); + ami_get_theme_filename(reload,"theme_reload",false); + ami_get_theme_filename(reload_s,"theme_reload_s",false); + ami_get_theme_filename(reload_g,"theme_reload_g",false); + ami_get_theme_filename(home,"theme_home",false); + ami_get_theme_filename(home_s,"theme_home_s",false); + ami_get_theme_filename(home_g,"theme_home_g",false); + ami_get_theme_filename(closetab,"theme_closetab",false); + ami_get_theme_filename(closetab_s,"theme_closetab_s",false); + ami_get_theme_filename(closetab_g,"theme_closetab_g",false); + ami_get_theme_filename(addtab,"theme_addtab",false); + ami_get_theme_filename(addtab_s,"theme_addtab_s",false); + ami_get_theme_filename(addtab_g,"theme_addtab_g",false); + ami_get_theme_filename(tabthrobber,"theme_tab_loading",false); gwin->shared->objects[GID_ADDTAB_BM] = BitMapObject, BITMAP_SourceFile, addtab, @@ -2482,7 +2484,7 @@ struct gui_window *gui_create_browser_window(struct browser_window *bw, IDCMP_GADGETUP | IDCMP_IDCMPUPDATE | IDCMP_ACTIVEWINDOW | IDCMP_INTUITICKS | IDCMP_EXTENDEDMOUSE | IDCMP_GADGETDOWN, - WINDOW_NewMenu,menu, + WINDOW_NewMenu, gwin->shared->menu, WINDOW_VertProp,1, WINDOW_IDCMPHook,&gwin->shared->scrollerhook, WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE | @@ -2934,6 +2936,7 @@ void gui_window_destroy(struct gui_window *g) DisposeObject(g->shared->objects[OID_MAIN]); + ami_free_menulabs(g->shared); free(g->shared->wintitle); ami_utf8_free(g->shared->status); FreeVec(g->shared->svbuffer); diff --git a/amiga/gui.h b/amiga/gui.h index dce118b89..70c1d2db3 100755 --- a/amiga/gui.h +++ b/amiga/gui.h @@ -23,8 +23,10 @@ #include <intuition/classusr.h> #include "desktop/browser.h" #include <dos/dos.h> +#include <devices/inputevent.h> #include "desktop/gui.h" #include "amiga/plotters.h" +#include "amiga/menu.h" enum { @@ -56,11 +58,7 @@ enum GID_PASS, GID_LOGIN, GID_CANCEL, - GID_TREEBROWSER, GID_OPEN, - GID_LEFT, - GID_UP, - GID_DOWN, GID_NEWF, GID_NEWB, GID_DEL, @@ -98,6 +96,11 @@ struct gui_window_2 { ULONG oldv; bool redraw_scroll; bool new_content; + char *menulab[AMI_MENU_AREXX_MAX + 1]; + char menukey[AMI_MENU_AREXX_MAX + 1]; + UBYTE *menutype; + struct NewMenu *menu; + ULONG hotlist_items; char *svbuffer; char *status; char *wintitle; @@ -127,6 +130,7 @@ void ami_close_all_tabs(struct gui_window_2 *gwin); void ami_quit_netsurf(void); void ami_do_redraw(struct gui_window_2 *g); STRPTR ami_locale_langs(void); +int ami_key_to_nskey(ULONG keycode, struct InputEvent *ie); struct box *ami_text_box_at_point(struct gui_window_2 *gwin, ULONG *x, ULONG *y); struct gui_window_2 *ami_window_at_pointer(void); diff --git a/amiga/history.c b/amiga/history.c index 5e685b76a..19ba9aff9 100755 --- a/amiga/history.c +++ b/amiga/history.c @@ -1,6 +1,5 @@ /* - * Copyright 2005 Richard Wilson <info@tinct.net> - * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,362 +16,24 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "desktop/browser.h" #include "amiga/history.h" #include "amiga/tree.h" -#include "content/urldb.h" -#include <proto/dos.h> -#include "amiga/options.h" +#include "desktop/history_global_core.h" #include <proto/exec.h> -#include <assert.h> -#include <utils/log.h> - -#define MAXIMUM_URL_LENGTH 1024 -#define MAXIMUM_BASE_NODES 16 - -static struct node *global_history_base_node[MAXIMUM_BASE_NODES]; -static int global_history_base_node_time[MAXIMUM_BASE_NODES]; -static int global_history_base_node_count = 0; - -static char *global_history_recent_url[GLOBAL_HISTORY_RECENT_URLS]; -static int global_history_recent_count = 0; - -static bool global_history_init; - -static struct node *ami_global_history_find(const char *url); -static bool global_history_add_internal(const char *url, - const struct url_data *data); -void ami_global_history_initialise_node(const char *title, - time_t base, int days_back); -void ami_global_history_initialise_nodes(void); +#include "amiga/tree.h" void ami_global_history_initialise(void) { - char s[MAXIMUM_URL_LENGTH]; - BPTR *fp; - -// if(global_history_tree) return; - - /* Create an empty tree */ - global_history_tree = AllocVec(sizeof(struct tree), MEMF_CLEAR | MEMF_PRIVATE); - if (!global_history_tree) { - warn_user("NoMemory", 0); - return; - } - global_history_tree->root = tree_create_folder_node(NULL, "Root"); - if (!global_history_tree->root) { - warn_user("NoMemory", 0); - FreeVec(global_history_tree); - global_history_tree = NULL; - } - global_history_tree->root->expanded = true; - ami_global_history_initialise_nodes(); - global_history_tree->movable = false; - - /* load recent URLs */ - fp = FOpen(option_recent_file, MODE_OLDFILE,0); - if (!fp) - LOG(("Failed to open file '%s' for reading", - option_recent_file)); - else { - while (FGets(fp,s, MAXIMUM_URL_LENGTH)) { - if (s[strlen(s) - 1] == '\n') - s[strlen(s) - 1] = '\0'; - global_history_add_recent(s); - } - FClose(fp); - } - - global_history_init = true; - urldb_iterate_entries(global_history_add_internal); - global_history_init = false; - tree_initialise(global_history_tree); - -} - -void global_history_add(const char *url) -{ - const struct url_data *data; - - data = urldb_get_url_data(url); - if (!data) - return; - - global_history_add_internal(url, data); -} - -/** - * Internal routine to actually perform global history addition - * - * \param url The URL to add - * \param data URL data associated with URL - * \return true (for urldb_iterate_entries) - */ -bool global_history_add_internal(const char *url, - const struct url_data *data) -{ - int i, j; - struct node *parent = NULL; - struct node *link; - struct node *node; - bool before = false; - int visit_date; - - assert(url && data); - - visit_date = data->last_visit; - - /* find parent node */ - for (i = 0; i < global_history_base_node_count; i++) { - if (global_history_base_node_time[i] <= visit_date) { - parent = global_history_base_node[i]; - break; - } - } - - /* the entry is too old to care about */ - if (!parent) - return true; - - if (parent->deleted) { - /* parent was deleted, so find place to insert it */ - link = global_history_tree->root; - - for (j = global_history_base_node_count - 1; j >= 0; j--) { - if (!global_history_base_node[j]->deleted && - global_history_base_node_time[j] > - global_history_base_node_time[i]) { - link = global_history_base_node[j]; - before = true; - break; - } - } + global_history_window = ami_tree_create(history_global_get_tree_flags(), NULL); - tree_set_node_selected(global_history_tree, - parent, false); - tree_set_node_expanded(global_history_tree, - parent, false); - tree_link_node(link, parent, before); -/* - if (!global_history_init) { - tree_recalculate_node(global_history_tree, parent, true); - tree_recalculate_node_positions(global_history_tree, - global_history_tree->root); - tree_redraw_area(global_history_tree, - 0, 0, 16384, 16384); - } -*/ - } - - /* find any previous occurance */ + if(!global_history_window) return; - if (!global_history_init) { - node = ami_global_history_find(url); - if (node) { - /* \todo: calculate old/new positions and redraw - * only the relevant portion */ -/* - tree_redraw_area(global_history_tree, - 0, 0, 16384, 16384); -*/ - tree_update_URL_node(node, url, data); - tree_delink_node(node); - tree_link_node(parent, node, false); - tree_handle_node_changed(global_history_tree, - node, false, true); - return true; - } - } - - /* Add the node at the bottom */ - node = tree_create_URL_node_shared(parent, url, data); - if ((!global_history_init) && (node)) { -/* - tree_redraw_area(global_history_tree, - node->box.x - NODE_INSTEP, - 0, NODE_INSTEP, 16384); -*/ - tree_handle_node_changed(global_history_tree, node, - true, false); - } - - return true; -} - -/** - * Find an entry in the global history - * - * \param url The URL to find - * \return Pointer to node, or NULL if not found - */ -struct node *ami_global_history_find(const char *url) -{ - int i; - struct node *node; - struct node_element *element; - - for (i = 0; i < global_history_base_node_count; i++) { - if (!global_history_base_node[i]->deleted) { - for (node = global_history_base_node[i]->child; - node; node = node->next) { - element = tree_find_element(node, - TREE_ELEMENT_URL); - if ((element) && !strcmp(url, element->text)) - return node; - } - } - } - return NULL; -} - -/** - * Saves the global history's recent URL data. - */ -void ami_global_history_save(void) -{ - BPTR *fp; - int i; - - /* save recent URLs */ - fp = fopen(option_recent_file, "w"); - if (!fp) - LOG(("Failed to open file '%s' for writing", - option_recent_file)); - else { - for (i = global_history_recent_count - 1; i >= 0; i--) - if (strlen(global_history_recent_url[i]) < - MAXIMUM_URL_LENGTH) - fprintf(fp, "%s\n", - global_history_recent_url[i]); - fclose(fp); - } -} - -void global_history_add_recent(const char *url) -{ - int i; - int j = -1; - char *current; - - /* try to find a string already there */ - for (i = 0; i < global_history_recent_count; i++) - if (global_history_recent_url[i] && - !strcmp(global_history_recent_url[i], url)) - j = i; - - /* already at head of list */ - if (j == 0) - return; - - if (j < 0) { - /* add to head of list */ - free(global_history_recent_url[ - GLOBAL_HISTORY_RECENT_URLS - 1]); - memmove(&global_history_recent_url[1], - &global_history_recent_url[0], - (GLOBAL_HISTORY_RECENT_URLS - 1) * - sizeof(char *)); - global_history_recent_url[0] = strdup(url); - global_history_recent_count++; - if (global_history_recent_count > GLOBAL_HISTORY_RECENT_URLS) - global_history_recent_count = - GLOBAL_HISTORY_RECENT_URLS; -/* - if (global_history_recent_count == 1) - ro_gui_window_prepare_navigate_all(); -*/ - } else { - /* move to head of list */ - current = global_history_recent_url[j]; - for (i = j; i > 0; i--) - global_history_recent_url[i] = - global_history_recent_url[i - 1]; - global_history_recent_url[0] = current; - } -} - -/** - * Gets details of the currently used URL list. - * - * \param count set to the current number of entries in the URL array on exit - * \return the current URL array - */ -char **global_history_get_recent(int *count) -{ - *count = global_history_recent_count; - return global_history_recent_url; + history_global_initialise(ami_tree_get_tree(global_history_window)); } void ami_global_history_free() { - FreeVec(global_history_tree); -} - -/** - * Initialises the base nodes - */ -void ami_global_history_initialise_nodes(void) -{ - struct tm *full_time; - time_t t; - int weekday; - int i; - - /* get the current time */ - t = time(NULL); - if (t == -1) - return; - - /* get the time at the start of today */ - full_time = localtime(&t); - weekday = full_time->tm_wday; - full_time->tm_sec = 0; - full_time->tm_min = 0; - full_time->tm_hour = 0; - t = mktime(full_time); - if (t == -1) - return; - - ami_global_history_initialise_node((char *)messages_get("DateToday"), t, 0); - if (weekday > 0) - ami_global_history_initialise_node( - (char *)messages_get("DateYesterday"), t, -1); - for (i = 2; i <= weekday; i++) - ami_global_history_initialise_node(NULL, t, -i); - ami_global_history_initialise_node((char *)messages_get("Date1Week"), - t, -weekday - 7); - ami_global_history_initialise_node((char *)messages_get("Date2Week"), - t, -weekday - 14); - ami_global_history_initialise_node((char *)messages_get("Date3Week"), - t, -weekday - 21); + history_global_cleanup(); + ami_tree_destroy(global_history_window); + global_history_window = NULL; } - -/** - * Create and initialise a node - */ -void ami_global_history_initialise_node(const char *title, - time_t base, int days_back) -{ - struct tm *full_time; - char buffer[64]; - struct node *node; - - base += days_back * 60 * 60 * 24; - if (!title) { - full_time = localtime(&base); - strftime((char *)&buffer, (size_t)64, "%A", full_time); - node = tree_create_folder_node(NULL, buffer); - } else - node = tree_create_folder_node(NULL, title); - - if (!node) - return; - - node->retain_in_memory = true; - node->deleted = true; - node->editable = false; - global_history_base_node[global_history_base_node_count] = node; - global_history_base_node_time[global_history_base_node_count] = base; - global_history_base_node_count++; -} - diff --git a/amiga/history.h b/amiga/history.h index c9f8254dc..b69d46c6a 100755 --- a/amiga/history.h +++ b/amiga/history.h @@ -19,11 +19,12 @@ #ifndef AMIGA_HISTORY_H #define AMIGA_HISTORY_H #include "desktop/tree.h" +#include "desktop/history_global_core.h" #define GLOBAL_HISTORY_RECENT_URLS 16 void ami_global_history_initialise(void); void ami_global_history_free(void); -struct tree *global_history_tree; +struct treeview_window *global_history_window; #endif diff --git a/amiga/hotlist.c b/amiga/hotlist.c index 71c5ddbba..f90f3645e 100755 --- a/amiga/hotlist.c +++ b/amiga/hotlist.c @@ -1,5 +1,4 @@ /* - * Copyright 2004, 2005 Richard Wilson <info@tinct.net> * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ @@ -17,137 +16,24 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "desktop/browser.h" -#include "desktop/tree.h" #include <proto/exec.h> -#include "content/urldb.h" #include "amiga/hotlist.h" +#include "desktop/hotlist.h" #include "amiga/tree.h" -void ami_gui_hotlist_visited(hlcache_handle *content, struct tree *tree, - struct node *node); - -static const struct { - const char *url; - const char *msg_key; -} default_entries[] = { - { "http://www.netsurf-browser.org/", "HotlistHomepage" }, - { "http://www.netsurf-browser.org/downloads/amiga/", "HotlistTestBuild" }, - { "http://www.netsurf-browser.org/documentation/", "HotlistDocumentation" }, - { "http://sourceforge.net/tracker/?atid=464312&group_id=51719", - "HotlistBugTracker" }, - { "http://sourceforge.net/tracker/?atid=464315&group_id=51719", - "HotlistFeatureRequest" }, - { "http://www.unsatisfactorysoftware.co.uk/", - "Unsatisfactory Software" } -}; -#define ENTRIES_COUNT (sizeof(default_entries) / sizeof(default_entries[0])) - -void hotlist_visited(hlcache_handle *c) +void ami_hotlist_initialise(const char *hotlist_file) { - if ((!c) || (!content_get_url(c)) || (!hotlist)) - return; - ami_gui_hotlist_visited(c, hotlist, hotlist->root); -} + hotlist_window = ami_tree_create(hotlist_get_tree_flags(), NULL); -/** - * 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 ami_gui_hotlist_visited(hlcache_handle *content, struct tree *tree, - struct node *node) -{ - struct node_element *element; + if(!hotlist_window) return; - for (; node; node = node->next) { - if (!node->folder) { - element = tree_find_element(node, TREE_ELEMENT_URL); - if ((element) && (!strcmp(element->text, - content_get_url(content)))) { - tree_update_URL_node(node, content_get_url(content), NULL); - tree_handle_node_changed(tree, node, true, - false); - } - } - if (node->child) - ami_gui_hotlist_visited(content, tree, node->child); - } + hotlist_initialise(ami_tree_get_tree(hotlist_window), + hotlist_file); } -void ami_hotlist_init(struct tree **hotlist) +void ami_hotlist_free(const char *hotlist_file) { - struct tree *hotlist_tree; - struct node *node; - int i; - const struct url_data *data; - - *hotlist = AllocVec(sizeof(struct tree),MEMF_PRIVATE | MEMF_CLEAR); - hotlist_tree = *hotlist; - - 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); - FreeVec(hotlist_tree); - hotlist_tree = NULL; - } - - hotlist_tree->root->expanded = true; - - node = tree_create_folder_node(hotlist_tree->root, "Menu"); - if (!node) - node = hotlist_tree->root; - - node = tree_create_folder_node(node, "NetSurf"); - if (!node) - node = hotlist_tree->root; - - for (i = 0; i != ENTRIES_COUNT; i++) { - data = urldb_get_url_data(default_entries[i].url); - if (!data) { - urldb_add_url(default_entries[i].url); - urldb_set_url_persistence( - default_entries[i].url, - true); - data = urldb_get_url_data( - default_entries[i].url); - } - if (data) { - tree_create_URL_node(node, - default_entries[i].url, data, - messages_get(default_entries[i].msg_key)); - } - } - - tree_initialise(hotlist_tree); -} - -void ami_hotlist_add(struct node *node, struct hlcache_handle *c) -{ - const struct url_data *data; - - data = urldb_get_url_data(content_get_url(c)); - if (!data) - { - urldb_add_url(content_get_url(c)); - urldb_set_url_persistence(content_get_url(c),true); - data = urldb_get_url_data(content_get_url(c)); - } - - if (data) - { - tree_create_URL_node(node,content_get_url(c),data,content_get_title(c)); - } - - tree_handle_node_changed(hotlist,node,false,true); - - if(hotlist->handle) - ami_recreate_listbrowser((struct treeview_window *)hotlist->handle); + hotlist_cleanup(hotlist_file); + ami_tree_destroy(hotlist_window); + hotlist_window = NULL; } diff --git a/amiga/hotlist.h b/amiga/hotlist.h index eed5b0eed..ee258af7b 100755 --- a/amiga/hotlist.h +++ b/amiga/hotlist.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -19,9 +19,10 @@ #ifndef AMIGA_HOTLIST_H #define AMIGA_HOTLIST_H #include "desktop/tree.h" +#include "amiga/tree.h" -void ami_hotlist_init(struct tree **hotlist); -void ami_hotlist_add(struct node *node, struct hlcache_handle *c); +void ami_hotlist_initialise(const char *hotlist_file); +void ami_hotlist_free(const char *hotlist_file); -struct tree *hotlist; +struct treeview_window *hotlist_window; #endif diff --git a/amiga/menu.c b/amiga/menu.c index 9f5aab757..081b1e568 100755 --- a/amiga/menu.c +++ b/amiga/menu.c @@ -41,6 +41,11 @@ #include "amiga/iff_dr2d.h" #include "amiga/clipboard.h" #include "amiga/gui_options.h" +#include "desktop/tree_url_node.h" +#include <classes/window.h> +#include <proto/intuition.h> +#include "desktop/hotlist.h" +#include <proto/gadtools.h> #include "amiga/print.h" #include "amiga/download.h" @@ -61,226 +66,221 @@ const char * const versvn; const char * const verdate; static struct Hook aslhookfunc; -void ami_menu_scan(struct tree *tree,struct NewMenu *menu); -void ami_menu_scan_2(struct tree *tree,struct node *root,WORD *gen,ULONG *item,struct NewMenu *menu); -void ami_menu_arexx_scan(struct NewMenu *menu); +ULONG ami_menu_scan(struct tree *tree, bool count, struct gui_window_2 *gwin); +void ami_menu_scan_2(struct tree *tree, struct node *root, WORD *gen, + ULONG *item, bool count, struct gui_window_2 *gwin); +void ami_menu_arexx_scan(struct gui_window_2 *gwin); static const ULONG ami_asl_mime_hook(struct Hook *mh,struct FileRequester *fr,struct AnchorPathOld *ap); -void ami_free_menulabs(void) +void ami_free_menulabs(struct gui_window_2 *gwin) { int i; - for(i=0;i<=AMI_MENU_MAX;i++) + for(i=0;i<=AMI_MENU_AREXX_MAX;i++) { - if(menulab[i] && (menulab[i] != NM_BARLABEL)) ami_utf8_free(menulab[i]); + if(gwin->menulab[i] && (gwin->menulab[i] != NM_BARLABEL)) + ami_utf8_free(gwin->menulab[i]); + + gwin->menulab[i] = NULL; + gwin->menukey[i] = 0; } - for(i=AMI_MENU_MAX+1;i<=AMI_MENU_AREXX_MAX;i++) + FreeVec(gwin->menutype); + FreeVec(gwin->menu); + + gwin->menutype = NULL; + gwin->menu = NULL; +} + +void ami_init_menulabs(struct gui_window_2 *gwin) +{ + int i; + + gwin->menutype = AllocVec(AMI_MENU_AREXX_MAX + 1, MEMF_PRIVATE | MEMF_CLEAR); + + for(i=0;i <= AMI_MENU_AREXX_MAX;i++) { - if(menulab[i] && (menulab[i] != NM_BARLABEL)) free(menulab[i]); + gwin->menutype[i] = NM_IGNORE; + gwin->menulab[i] = NULL; } + + gwin->menutype[0] = NM_TITLE; + gwin->menulab[0] = ami_utf8_easy((char *)messages_get("Project")); + gwin->menutype[1] = NM_ITEM; + gwin->menulab[1] = ami_utf8_easy((char *)messages_get("NewWindowNS")); + gwin->menukey[1] = 'N'; + gwin->menutype[2] = NM_ITEM; + gwin->menulab[2] = ami_utf8_easy((char *)messages_get("NewTab")); + gwin->menukey[1] = 'T'; + gwin->menutype[3] = NM_ITEM; + gwin->menulab[3] = NM_BARLABEL; + gwin->menutype[4] = NM_ITEM; + gwin->menulab[4] = ami_utf8_easy((char *)messages_get("OpenFile")); + gwin->menukey[4] = 'O'; + gwin->menutype[5] = NM_ITEM; + gwin->menulab[5] = ami_utf8_easy((char *)messages_get("SaveAsNS")); + gwin->menukey[5] = 'S'; + gwin->menutype[6] = NM_SUB; + gwin->menulab[6] = ami_utf8_easy((char *)messages_get("Source")); + gwin->menutype[7] = NM_SUB; + gwin->menulab[7] = ami_utf8_easy((char *)messages_get("TextNS")); + gwin->menutype[8] = NM_SUB; + gwin->menulab[8] = ami_utf8_easy((char *)messages_get("SaveCompNS")); + gwin->menutype[9] = NM_SUB; + gwin->menulab[9] = ami_utf8_easy((char *)messages_get("PDFNS")); + gwin->menutype[10] = NM_SUB; + gwin->menulab[10] = ami_utf8_easy((char *)messages_get("IFF")); + gwin->menutype[11] = NM_ITEM; + gwin->menulab[11] = NM_BARLABEL; + gwin->menutype[12] = NM_ITEM; + gwin->menulab[12] = ami_utf8_easy((char *)messages_get("CloseTab")); + gwin->menukey[12] = 'K'; + gwin->menutype[13] = NM_ITEM; + gwin->menulab[13] = ami_utf8_easy((char *)messages_get("CloseWindow")); + gwin->menutype[14] = NM_ITEM; + gwin->menulab[14] = NM_BARLABEL; + gwin->menutype[15] = NM_ITEM; + gwin->menulab[15] = ami_utf8_easy((char *)messages_get("PrintNS")); + gwin->menukey[15] = 'P'; + gwin->menutype[16] = NM_ITEM; + gwin->menulab[16] = NM_BARLABEL; + gwin->menutype[17] = NM_ITEM; + gwin->menulab[17] = ami_utf8_easy((char *)messages_get("About")); + gwin->menukey[17] = '?'; + gwin->menutype[18] = NM_ITEM; + gwin->menulab[18] = ami_utf8_easy((char *)messages_get("Quit")); + gwin->menukey[18] = 'Q'; + gwin->menutype[19] = NM_TITLE; + gwin->menulab[19] = ami_utf8_easy((char *)messages_get("Edit")); + gwin->menutype[20] = NM_ITEM; + gwin->menulab[20] = ami_utf8_easy((char *)messages_get("CutNS")); + gwin->menukey[20] = 'X'; + gwin->menutype[21] = NM_ITEM; + gwin->menulab[21] = ami_utf8_easy((char *)messages_get("CopyNS")); + gwin->menukey[21] = 'C'; + gwin->menutype[22] = NM_ITEM; + gwin->menulab[22] = ami_utf8_easy((char *)messages_get("PasteNS")); + gwin->menukey[22] = 'V'; + gwin->menutype[23] = NM_ITEM; + gwin->menulab[23] = NM_BARLABEL; + gwin->menutype[24] = NM_ITEM; + gwin->menulab[24] = ami_utf8_easy((char *)messages_get("SelectAllNS")); + gwin->menukey[24] = 'A'; + gwin->menutype[25] = NM_ITEM; + gwin->menulab[25] = ami_utf8_easy((char *)messages_get("ClearNS")); + gwin->menukey[25] = 'Z'; + gwin->menutype[26] = NM_TITLE; + gwin->menulab[26] = ami_utf8_easy((char *)messages_get("Browser")); + gwin->menutype[27] = NM_ITEM; + gwin->menulab[27] = ami_utf8_easy((char *)messages_get("FindTextNS")); + gwin->menukey[27] = 'F'; + gwin->menutype[28] = NM_ITEM; + gwin->menulab[28] = NM_BARLABEL; + gwin->menutype[29] = NM_ITEM; + gwin->menulab[29] = ami_utf8_easy((char *)messages_get("HistLocalNS")); + gwin->menutype[30] = NM_ITEM; + gwin->menulab[30] = ami_utf8_easy((char *)messages_get("HistGlobalNS")); + gwin->menutype[31] = NM_ITEM; + gwin->menulab[31] = NM_BARLABEL; + gwin->menutype[32] = NM_ITEM; + gwin->menulab[32] = ami_utf8_easy((char *)messages_get("ShowCookies")); + gwin->menutype[33] = NM_ITEM; + gwin->menulab[33] = NM_BARLABEL; + gwin->menutype[34] = NM_ITEM; + gwin->menulab[34] = ami_utf8_easy((char *)messages_get("ScaleNS")); + gwin->menutype[35] = NM_SUB; + gwin->menulab[35] = ami_utf8_easy((char *)messages_get("ScaleDec")); + gwin->menukey[35] = '-'; + gwin->menutype[36] = NM_SUB; + gwin->menulab[36] = ami_utf8_easy((char *)messages_get("ScaleNorm")); + gwin->menukey[36] = '='; + gwin->menutype[37] = NM_SUB; + gwin->menulab[37] = ami_utf8_easy((char *)messages_get("ScaleInc")); + gwin->menukey[37] = '+'; + gwin->menutype[38] = NM_ITEM; + gwin->menulab[38] = ami_utf8_easy((char *)messages_get("Redraw")); + gwin->menutype[39] = NM_TITLE; + gwin->menulab[39] = ami_utf8_easy((char *)messages_get("Hotlist")); + gwin->menukey[39] = 'H'; + gwin->menutype[40] = NM_ITEM; + gwin->menulab[40] = ami_utf8_easy((char *)messages_get("HotlistAdd")); + gwin->menutype[41] = NM_ITEM; + gwin->menulab[41] = ami_utf8_easy((char *)messages_get("HotlistShowNS")); + gwin->menutype[42] = NM_ITEM; + gwin->menulab[42] = NM_BARLABEL; + + gwin->menutype[AMI_MENU_HOTLIST_MAX + 1] = NM_TITLE; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 1] = ami_utf8_easy((char *)messages_get("Settings")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 2] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 2] = ami_utf8_easy((char *)messages_get("SettingsEdit")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 3] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 3] = NM_BARLABEL; + gwin->menutype[AMI_MENU_HOTLIST_MAX + 4] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 4] = ami_utf8_easy((char *)messages_get("SnapshotWindow")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 5] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 5] = ami_utf8_easy((char *)messages_get("SettingsSave")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 6] = NM_TITLE; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 6] = ami_utf8_easy((char *)messages_get("ARexx")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 7] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 7] = ami_utf8_easy((char *)messages_get("ARexxExecute")); + gwin->menutype[AMI_MENU_HOTLIST_MAX + 8] = NM_ITEM; + gwin->menulab[AMI_MENU_HOTLIST_MAX + 8] = NM_BARLABEL; + + gwin->menutype[AMI_MENU_AREXX_MAX] = NM_END; } -void ami_init_menulabs(void) +void ami_menu_refresh(struct gui_window_2 *gwin) { - menulab[0] = ami_utf8_easy((char *)messages_get("Project")); - menulab[1] = ami_utf8_easy((char *)messages_get("NewWindowNS")); - menulab[2] = ami_utf8_easy((char *)messages_get("NewTab")); - menulab[3] = NM_BARLABEL; - menulab[4] = ami_utf8_easy((char *)messages_get("OpenFile")); - menulab[5] = ami_utf8_easy((char *)messages_get("SaveAsNS")); - menulab[6] = ami_utf8_easy((char *)messages_get("Source")); - menulab[7] = ami_utf8_easy((char *)messages_get("TextNS")); - menulab[8] = ami_utf8_easy((char *)messages_get("SaveCompNS")); - menulab[9] = ami_utf8_easy((char *)messages_get("PDFNS")); - menulab[10] = ami_utf8_easy((char *)messages_get("IFF")); - menulab[11] = NM_BARLABEL; - menulab[12] = ami_utf8_easy((char *)messages_get("CloseTab")); - menulab[13] = ami_utf8_easy((char *)messages_get("CloseWindow")); - menulab[14] = NM_BARLABEL; - menulab[15] = ami_utf8_easy((char *)messages_get("PrintNS")); - menulab[16] = NM_BARLABEL; - menulab[17] = ami_utf8_easy((char *)messages_get("About")); - menulab[18] = ami_utf8_easy((char *)messages_get("Quit")); - menulab[19] = ami_utf8_easy((char *)messages_get("Edit")); - menulab[20] = ami_utf8_easy((char *)messages_get("CutNS")); - menulab[21] = ami_utf8_easy((char *)messages_get("CopyNS")); - menulab[22] = ami_utf8_easy((char *)messages_get("PasteNS")); - menulab[23] = NM_BARLABEL; - menulab[24] = ami_utf8_easy((char *)messages_get("SelectAllNS")); - menulab[25] = ami_utf8_easy((char *)messages_get("ClearNS")); - menulab[26] = ami_utf8_easy((char *)messages_get("Browser")); - menulab[27] = ami_utf8_easy((char *)messages_get("FindTextNS")); - menulab[28] = NM_BARLABEL; - menulab[29] = ami_utf8_easy((char *)messages_get("HistLocalNS")); - menulab[30] = ami_utf8_easy((char *)messages_get("HistGlobalNS")); - menulab[31] = NM_BARLABEL; - menulab[32] = ami_utf8_easy((char *)messages_get("ShowCookies")); - menulab[33] = NM_BARLABEL; - menulab[34] = ami_utf8_easy((char *)messages_get("ScaleNS")); - menulab[35] = ami_utf8_easy((char *)messages_get("ScaleDec")); - menulab[36] = ami_utf8_easy((char *)messages_get("ScaleNorm")); - menulab[37] = ami_utf8_easy((char *)messages_get("ScaleInc")); - menulab[38] = ami_utf8_easy((char *)messages_get("Redraw")); - menulab[39] = ami_utf8_easy((char *)messages_get("Hotlist")); - menulab[40] = ami_utf8_easy((char *)messages_get("HotlistAdd")); - menulab[41] = ami_utf8_easy((char *)messages_get("HotlistShowNS")); - menulab[42] = NM_BARLABEL; - - menulab[AMI_MENU_HOTLIST_MAX] = ami_utf8_easy((char *)messages_get("Settings")); - menulab[AMI_MENU_HOTLIST_MAX+1] = ami_utf8_easy((char *)messages_get("SettingsEdit")); - menulab[AMI_MENU_HOTLIST_MAX+2] = NM_BARLABEL; - menulab[AMI_MENU_HOTLIST_MAX+3] = ami_utf8_easy((char *)messages_get("SnapshotWindow")); - menulab[AMI_MENU_HOTLIST_MAX+4] = ami_utf8_easy((char *)messages_get("SettingsSave")); - menulab[AMI_MENU_HOTLIST_MAX+5] = ami_utf8_easy((char *)messages_get("ARexx")); - menulab[AMI_MENU_HOTLIST_MAX+6] = ami_utf8_easy((char *)messages_get("ARexxExecute")); - menulab[AMI_MENU_HOTLIST_MAX+7] = NM_BARLABEL; + SetAttrs(gwin->objects[OID_MAIN], + WINDOW_NewMenu, NULL, + TAG_DONE); + + ami_free_menulabs(gwin); + ami_create_menu(BROWSER_WINDOW_NORMAL, gwin); + + SetAttrs(gwin->objects[OID_MAIN], + WINDOW_NewMenu, gwin->menu, + TAG_DONE); + + schedule(6000,(void *)ami_menu_refresh,gwin); } -struct NewMenu *ami_create_menu(ULONG type) +struct NewMenu *ami_create_menu(ULONG type, struct gui_window_2 *gwin) { int i; ULONG menuflags = 0; - STATIC struct NewMenu menu[] = { - {NM_TITLE,0,0,0,0,0,}, // project - { NM_ITEM,0,"N",0,0,0,}, // new window - { NM_ITEM,0,"T",0,0,0,}, // new tab - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,"O",0,0,0,}, // open local file - { NM_ITEM,0,0,0,0,0,}, // save - { NM_SUB,0,"S",0,0,0,}, // save as source - { NM_SUB,0,0,0,0,0,}, // save as text - { NM_SUB,0,0,0,0,0,}, // save as complete - { NM_SUB,0,0,0,0,0,}, // save as pdf - { NM_SUB,0,0,0,0,0,}, // save as iff - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,"K",0,0,0,}, // close tab - { NM_ITEM,0,0,0,0,0,}, // close window - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,"P",0,0,0,}, // print - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,"?",0,0,0,}, // about - { NM_ITEM,0,"Q",0,0,0,}, // quit - {NM_TITLE,0,0,0,0,0,}, // edit - { NM_ITEM,0,"X",0,0,0,}, // cut - { NM_ITEM,0,"C",0,0,0,}, // copy - { NM_ITEM,0,"V",0,0,0,}, // paste - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,"A",0,0,0,}, // select all - { NM_ITEM,0,"Z",0,0,0,}, // clear selection - {NM_TITLE,0,0,0,0,0,}, // browser - { NM_ITEM,0,"F",0,0,0,}, // find in page - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,0,0,0,0,}, // local history - { NM_ITEM,0,0,0,0,0,}, // global history - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,0,0,0,0,}, // cookies - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,0,0,0,0,}, // scale - { NM_SUB,0,"-",0,0,0,}, // decrease - { NM_SUB,0,"=",0,0,0,}, // normal - { NM_SUB,0,"+",0,0,0,}, // increase - { NM_ITEM,0,0,0,0,0,}, // redraw - {NM_TITLE,0,0,0,0,0,}, // hotlist - { NM_ITEM,0,0,0,0,0,}, // add to hotlist - { NM_ITEM,0,"H",0,0,0,}, // show hotlist (treeview) - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** hotlist entry ** - {NM_TITLE,0,0,0,0,0,}, // settings - { NM_ITEM,0,0,0,0,0,}, // edit prefs - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_ITEM,0,0,0,0,0,}, // snapshot window - { NM_ITEM,0,0,0,0,0,}, // save settings - {NM_TITLE,0,0,0,0,0,}, // arexx - { NM_ITEM,0,0,0,0,0,}, // execute arexx - { NM_ITEM,NM_BARLABEL,0,0,0,0,}, - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_IGNORE,0,0,0,0,0,}, // ** arexx entry ** - { NM_END,0,0,0,0,0,}, - }; + + ami_init_menulabs(gwin); + gwin->menu = AllocVec(sizeof(struct NewMenu) * (AMI_MENU_AREXX_MAX + 1), MEMF_CLEAR); if(type != BROWSER_WINDOW_NORMAL) { menuflags = NM_ITEMDISABLED; } - for(i=0;i<=AMI_MENU_MAX;i++) + for(i=0;i<=AMI_MENU_AREXX_MAX;i++) { - menu[i].nm_Label = menulab[i]; + gwin->menu[i].nm_Type = gwin->menutype[i]; + gwin->menu[i].nm_Label = gwin->menulab[i]; + if(gwin->menukey[i]) gwin->menu[i].nm_CommKey = &gwin->menukey[i]; + gwin->menu[i].nm_Flags = 0; } - menu[1].nm_Flags = menuflags; - menu[2].nm_Flags = menuflags; - menu[12].nm_Flags = menuflags; - menu[13].nm_Flags = menuflags; + gwin->menu[1].nm_Flags = menuflags; + gwin->menu[2].nm_Flags = menuflags; + gwin->menu[12].nm_Flags = menuflags; + gwin->menu[13].nm_Flags = menuflags; #ifndef WITH_PDF_EXPORT - menu[9].nm_Flags = NM_ITEMDISABLED; + gwin->menu[9].nm_Flags = NM_ITEMDISABLED; #endif + ami_menu_scan(ami_tree_get_tree(hotlist_window), false, gwin); + ami_menu_arexx_scan(gwin); + if(!menualreadyinit) { - ami_menu_scan(hotlist,menu); - ami_menu_arexx_scan(&menu); - aslhookfunc.h_Entry = (void *)&ami_asl_mime_hook; aslhookfunc.h_SubEntry = NULL; aslhookfunc.h_Data = NULL; @@ -288,10 +288,16 @@ struct NewMenu *ami_create_menu(ULONG type) menualreadyinit = TRUE; } - return(menu); +/* Set up scheduler to refresh the hotlist menu + Disabled as it causes everything to slow down to a halt after + several iterations + schedule(6000,(void *)ami_menu_refresh,gwin); +*/ + + return(gwin->menu); } -void ami_menu_arexx_scan(struct NewMenu *menu) +void ami_menu_arexx_scan(struct gui_window_2 *gwin) { int item = AMI_MENU_AREXX; BPTR lock = 0; @@ -323,23 +329,20 @@ void ami_menu_arexx_scan(struct NewMenu *menu) for(ead = (struct ExAllData *)buffer; ead; ead = ead->ed_Next) { if(item >= AMI_MENU_AREXX_MAX) continue; - if(!strcasecmp(ead->ed_Name, option_arexx_startup)) continue; - if(!strcasecmp(ead->ed_Name, option_arexx_shutdown)) continue; - if(EAD_IS_FILE(ead)) { - menu[item].nm_Type = NM_ITEM; + gwin->menu[item].nm_Type = NM_ITEM; if(ead->ed_Comment[0] != '\0') { - menulab[item] = (char *)strdup(ead->ed_Comment); + gwin->menulab[item] = (char *)strdup(ead->ed_Comment); } else { - menulab[item] = (char *)strdup(ead->ed_Name); + gwin->menulab[item] = (char *)strdup(ead->ed_Name); } - menu[item].nm_Label = menulab[item]; - menu[item].nm_UserData = (char *)strdup(ead->ed_Name); + gwin->menu[item].nm_Label = gwin->menulab[item]; + gwin->menu[item].nm_UserData = (char *)strdup(ead->ed_Name); item++; } @@ -351,31 +354,37 @@ void ami_menu_arexx_scan(struct NewMenu *menu) } UnLock(lock); } + + gwin->menu[item].nm_Type = NM_END; + gwin->menu[item].nm_Label = NULL; } -void ami_menu_scan(struct tree *tree,struct NewMenu *menu) +ULONG ami_menu_scan(struct tree *tree, bool count, struct gui_window_2 *gwin) { - struct node *root = tree->root->child; - struct node_element *element=NULL; + struct node *root = tree_node_get_child(tree_get_root(tree)); struct node *node; + struct node_element *element; static WORD gen = 0; static ULONG item; item = AMI_MENU_HOTLIST; - for (node = root; node; node = node->next) + for (node = root; node; node = tree_node_get_next(node)) { - element = tree_find_element(node, TREE_ELEMENT_NAME); - if(!element) element = tree_find_element(node, TREE_ELEMENT_TITLE); - if(element && (strcmp(element->text,"Menu")==0)) + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + if(!element) element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + if(element && (strcmp(tree_node_element_get_text(element),"Menu")==0)) { // found menu - ami_menu_scan_2(tree,node->child,&gen,&item,menu); + ami_menu_scan_2(tree,tree_node_get_child(node),&gen,&item,count,gwin); } } + + return(item - AMI_MENU_HOTLIST); } -void ami_menu_scan_2(struct tree *tree,struct node *root,WORD *gen,ULONG *item,struct NewMenu *menu) +void ami_menu_scan_2(struct tree *tree,struct node *root,WORD *gen, + ULONG *item, bool count, struct gui_window_2 *gwin) { struct node *tempnode; struct node_element *element=NULL; @@ -383,38 +392,39 @@ void ami_menu_scan_2(struct tree *tree,struct node *root,WORD *gen,ULONG *item,s *gen = *gen + 1; - for (node = root; node; node = node->next) + for (node = root; node; node = tree_node_get_next(node)) { - element = tree_find_element(node, TREE_ELEMENT_TITLE); - if((*gen > 0) && (*gen < 3)) { - if(*item >= AMI_MENU_HOTLIST_MAX) return; - - if(*gen == 1) menu[*item].nm_Type = NM_ITEM; - if(*gen == 2) menu[*item].nm_Type = NM_SUB; +// if(*item >= AMI_MENU_HOTLIST_MAX) return; - if(strcmp(element->text,"--")) - { - menulab[*item] = ami_utf8_easy((char *)element->text); - } - else + if(!count) { - menulab[*item] = NM_BARLABEL; - } + if(*gen == 1) gwin->menu[*item].nm_Type = NM_ITEM; + if(*gen == 2) gwin->menu[*item].nm_Type = NM_SUB; + + if(strcmp(tree_url_node_get_title(node),"--")) + { + gwin->menulab[*item] = ami_utf8_easy((char *)tree_url_node_get_title(node)); + } + else + { + gwin->menulab[*item] = NM_BARLABEL; + } - menu[*item].nm_Label = menulab[*item]; + gwin->menu[*item].nm_Label = gwin->menulab[*item]; - element = tree_find_element(node, TREE_ELEMENT_URL); - if(element && element->text) menu[*item].nm_UserData = (void *)element->text; - if(node->folder && (!node->child)) menu[*item].nm_Flags = NM_ITEMDISABLED; + gwin->menu[*item].nm_UserData = (void *)tree_url_node_get_url(node); + if(tree_node_is_folder(node) && (!tree_node_get_child(node))) + gwin->menu[*item].nm_Flags = NM_ITEMDISABLED; + } *item = *item + 1; } - if (node->child) + if (tree_node_get_child(node)) { - ami_menu_scan_2(tree,node->child,gen,item,menu); + ami_menu_scan_2(tree,tree_node_get_child(node),gen,item,count,gwin); } } @@ -698,11 +708,11 @@ void ami_menupick(ULONG code,struct gui_window_2 *gwin,struct MenuItem *item) break; case 3: // global history - ami_open_tree(global_history_tree,AMI_TREE_HISTORY); + ami_tree_open(global_history_window,AMI_TREE_HISTORY); break; case 5: // cookies tree - ami_open_tree(cookies_tree,AMI_TREE_COOKIES); + ami_tree_open(cookies_window,AMI_TREE_COOKIES); break; case 7: // size @@ -736,12 +746,18 @@ void ami_menupick(ULONG code,struct gui_window_2 *gwin,struct MenuItem *item) switch(itemnum) { case 0: // add - ami_hotlist_add(hotlist->root,gwin->bw->current_content); - options_save_tree(hotlist,option_hotlist_file,messages_get("TreeHotlist")); + if (bw == NULL || + bw->current_content + == NULL || + content_get_url(bw->current_content) + == NULL) + break; + hotlist_add_page(content_get_url(gwin->bw-> + current_content)); break; case 1: // show - ami_open_tree(hotlist,AMI_TREE_HOTLIST); + ami_tree_open(hotlist_window, AMI_TREE_HOTLIST); break; default: // bookmarks diff --git a/amiga/menu.h b/amiga/menu.h index 8f925411a..5a739d616 100755 --- a/amiga/menu.h +++ b/amiga/menu.h @@ -19,12 +19,14 @@ #ifndef AMIGA_MENU_H #define AMIGA_MENU_H #include <exec/types.h> -#include "amiga/gui.h" #include <intuition/intuition.h> +#include <libraries/gadtools.h> +#include "content/hlcache.h" /* Number of hotlist items, menu structure needs to be changed in ami_create_menu() * if this value is changed. */ -#define AMI_HOTLIST_ITEMS 40 +#define AMI_HOTLIST_ITEMS 60 +//gwin->hotlist_items /* Maximum number of menu items - first value is number of static items * (ie. everything not intially defined as NM_IGNORE) */ @@ -61,11 +63,12 @@ #define AMI_MENU_CLEAR FULLMENUNUM(1,5,0) #define AMI_MENU_FIND FULLMENUNUM(2,0,0) -char *menulab[AMI_MENU_AREXX_MAX+1]; +struct gui_window; +struct gui_window_2; -struct NewMenu *ami_create_menu(ULONG type); -void ami_init_menulabs(void); -void ami_free_menulabs(void); +void ami_free_menulabs(struct gui_window_2 *gwin); +struct NewMenu *ami_create_menu(ULONG type, struct gui_window_2 *gwin); +void ami_menu_refresh(struct gui_window_2 *gwin); void ami_menupick(ULONG code,struct gui_window_2 *gwin,struct MenuItem *item); void ami_menu_update_disabled(struct gui_window *g, hlcache_handle *c); #endif diff --git a/amiga/object.c b/amiga/object.c index a51be81c1..93094cbda 100755 --- a/amiga/object.c +++ b/amiga/object.c @@ -48,14 +48,24 @@ struct nsObject *AddObject(struct MinList *objlist,ULONG otype) return(dtzo); } -void DelObject(struct nsObject *dtzo) +void DelObjectInternal(struct nsObject *dtzo, BOOL free_obj) { Remove((struct Node *)dtzo); - if(dtzo->objstruct) FreeVec(dtzo->objstruct); + if(dtzo->objstruct && free_obj) FreeVec(dtzo->objstruct); FreeVec(dtzo); dtzo = NULL; } +void DelObject(struct nsObject *dtzo) +{ + DelObjectInternal(dtzo, TRUE); +} + +void DelObjectNoFree(struct nsObject *dtzo) +{ + DelObjectInternal(dtzo, FALSE); +} + void FreeObjList(struct MinList *objlist) { struct nsObject *node; diff --git a/amiga/object.h b/amiga/object.h index 82d01098d..db7869df8 100755 --- a/amiga/object.h +++ b/amiga/object.h @@ -48,6 +48,7 @@ struct nsObject struct MinList *NewObjList(void); struct nsObject *AddObject(struct MinList *objlist,ULONG otype); void DelObject(struct nsObject *dtzo); +void DelObjectNoFree(struct nsObject *dtzo); void FreeObjList(struct MinList *objlist); #endif diff --git a/amiga/options.h b/amiga/options.h index 7e4e6cc15..f32938969 100644 --- a/amiga/options.h +++ b/amiga/options.h @@ -34,7 +34,6 @@ extern bool option_truecolour_mouse_pointers; extern bool option_use_os_pointers; extern bool option_new_tab_active; extern bool option_kiosk_mode; -extern char *option_recent_file; extern char *option_search_engines_file; extern char *option_search_ico_file; extern char *option_arexx_dir; @@ -68,7 +67,6 @@ bool option_truecolour_mouse_pointers = false; \ bool option_use_os_pointers = true; \ bool option_new_tab_active = false; \ bool option_kiosk_mode = false; \ -char *option_recent_file = 0; \ char *option_search_engines_file = 0; \ char *option_search_ico_file = 0; \ char *option_arexx_dir = 0; \ @@ -102,7 +100,6 @@ bool option_drag_save_icons = true; \ { "os_mouse_pointers", OPTION_BOOL, &option_use_os_pointers}, \ { "new_tab_is_active", OPTION_BOOL, &option_new_tab_active}, \ { "kiosk_mode", OPTION_BOOL, &option_kiosk_mode}, \ -{ "recent_file", OPTION_STRING, &option_recent_file }, \ { "search_engines_file", OPTION_STRING, &option_search_engines_file }, \ { "search_ico_file", OPTION_STRING, &option_search_ico_file }, \ { "arexx_dir", OPTION_STRING, &option_arexx_dir }, \ diff --git a/amiga/plotters.c b/amiga/plotters.c index de60b9518..5bd34d2bc 100755 --- a/amiga/plotters.c +++ b/amiga/plotters.c @@ -91,7 +91,7 @@ void ami_cairo_set_colour(cairo_t *cr,colour c) void ami_cairo_set_solid(cairo_t *cr) { double dashes = 0; - + cairo_set_dash(glob->cr, &dashes, 0, 0); } @@ -240,7 +240,7 @@ bool ami_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) glob->rp.LinePtrn = PATT_DASH; break; } - + SetRPAttrs(&glob->rp, RPTAG_APenColor, p96EncodeColor(RGBFB_A8B8G8R8, style->stroke_colour), @@ -272,7 +272,7 @@ bool ami_rectangle(int x0, int y0, int x1, int y1, const plot_style_t *style) break; } - if (style->stroke_width == 0) + if (style->stroke_width == 0) cairo_set_line_width(glob->cr, 1); else cairo_set_line_width(glob->cr, style->stroke_width); @@ -337,13 +337,17 @@ bool ami_line(int x0, int y0, int x1, int y1, const plot_style_t *style) break; } - if (style->stroke_width == 0) + if (style->stroke_width == 0) cairo_set_line_width(glob->cr, 1); else cairo_set_line_width(glob->cr, style->stroke_width); - cairo_move_to(glob->cr, x0 + 0.5, y0 + 0.5); - cairo_line_to(glob->cr, x1 + 0.5, y1 + 0.5); + /* core expects horizontal and vertical lines to be on pixels, not + * between pixels */ + cairo_move_to(current_cr, (x0 == x1) ? x0 + 0.5 : x0, + (y0 == y1) ? y0 + 0.5 : y0); + cairo_line_to(current_cr, (x0 == x1) ? x1 + 0.5 : x1, + (y0 == y1) ? y1 + 0.5 : y1); cairo_stroke(glob->cr); #endif return true; @@ -426,7 +430,7 @@ bool ami_clip(int x0, int y0, int x1, int y1) return true; } -bool ami_text(int x, int y, const char *text, size_t length, +bool ami_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle) { #ifdef AMI_PLOTTER_DEBUG @@ -459,7 +463,7 @@ bool ami_disc(int x, int y, int radius, const plot_style_t *style) p96EncodeColor(RGBFB_A8B8G8R8, style->stroke_colour), TAG_DONE); - DrawEllipse(&glob->rp,x,y,radius,radius); + DrawEllipse(&glob->rp,x,y,radius,radius); } #else if (style->fill_type != PLOT_OP_TYPE_NONE) { diff --git a/amiga/resources/Themes/AISS/Theme b/amiga/resources/Themes/AISS/Theme index 8f3dd8aec..8dfa1d3a7 100755 --- a/amiga/resources/Themes/AISS/Theme +++ b/amiga/resources/Themes/AISS/Theme @@ -24,12 +24,8 @@ theme_closetab_g:*TBImages:list_cancel theme_addtab:*TBImages:list_add theme_addtab_s:*TBImages:list_add theme_addtab_g:*TBImages:list_add -theme_list_folder_closed:*TBImages:list_folderfold -theme_list_folder_open:*TBImages:list_folderunfold -theme_list_bookmark:*TBImages:list_bookmark -theme_list_cookie:*TBImages:list_abstract -theme_list_history:*TBImages:list_archive -theme_list_sslcert:*TBImages:list_crypt +theme_list_folder:*TBImages:list_drawer +theme_list_content:*TBImages:list_abstract theme_throbber:Throbber theme_throbber_frames:13 theme_throbber_delay:100 diff --git a/amiga/resources/Themes/Default/Theme b/amiga/resources/Themes/Default/Theme index eb6b9c3ce..2497af467 100755 --- a/amiga/resources/Themes/Default/Theme +++ b/amiga/resources/Themes/Default/Theme @@ -37,12 +37,8 @@ theme_closetab_g:closetab_g.png theme_addtab: theme_addtab_s: theme_addtab_g: -theme_list_folder_closed: -theme_list_folder_open: -theme_list_bookmark: -theme_list_cookie: -theme_list_history: -theme_list_sslcert: +theme_list_folder:directory.png +theme_list_content:content.png theme_throbber:Throbber theme_throbber_frames:9 theme_throbber_delay:100 diff --git a/amiga/resources/Themes/Default/content.png b/amiga/resources/Themes/Default/content.png new file mode 120000 index 000000000..4b8c13c1c --- /dev/null +++ b/amiga/resources/Themes/Default/content.png @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/Icons/content.png
\ No newline at end of file diff --git a/amiga/resources/Themes/Default/directory.png b/amiga/resources/Themes/Default/directory.png new file mode 120000 index 000000000..0e6ce854d --- /dev/null +++ b/amiga/resources/Themes/Default/directory.png @@ -0,0 +1 @@ +../../../../!NetSurf/Resources/Icons/directory.png
\ No newline at end of file diff --git a/amiga/sslcert.c b/amiga/sslcert.c index c02329449..8eede70f2 100644 --- a/amiga/sslcert.c +++ b/amiga/sslcert.c @@ -16,224 +16,28 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <assert.h> -#include <string.h> -#include <proto/dos.h> #include <proto/exec.h> -#include <proto/utility.h> -#include "utils/errors.h" -#include "utils/utils.h" -#include "utils/messages.h" -#include "content/urldb.h" -#include "content/fetch.h" -#include "desktop/tree.h" #include "amiga/tree.h" -#include "amiga/gui.h" - -struct session_data { - struct session_cert *certs; - unsigned long num; - nserror (*cb)(bool proceed, void *pw); - void *cbpw; - char *url; - struct tree *tree; -}; -struct session_cert { - char version[16], valid_from[32], valid_to[32], type[8], serial[32]; - char *issuer_t; - char *subject_t; - uintptr_t issuer; - uintptr_t subject; -}; - -void ami_gui_cert_close(struct session_data *data); -bool ami_gui_cert_apply(struct session_data *session); +#include "amiga/sslcert.h" void gui_cert_verify(const char *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw) { - const struct ssl_cert_info *from; - struct session_cert *to; - struct session_data *data; - struct tree *tree; - struct node *node; - long i; - STRPTR yesorno,reqcontents; - int res = 0; - struct treeview_window *twin; - - assert(certs); - - /* copy the certificate information */ - data = calloc(1, sizeof(struct session_data)); - if (!data) { - warn_user("NoMemory", 0); - return; - } - data->url = strdup(url); - if (!data->url) { - free(data); - warn_user("NoMemory", 0); - return; - } - data->cb = cb; - data->cbpw = cbpw; - data->num = num; - data->certs = calloc(num, sizeof(struct session_cert)); - if (!data->certs) { - free(data->url); - free(data); - warn_user("NoMemory", 0); - return; - } - for (i = 0; i < (long)num; i++) { - to = &data->certs[i]; - from = &certs[i]; - to->subject_t = strdup(from->subject); - to->issuer_t = strdup(from->issuer); - if ((!to->subject_t) || (!to->issuer_t)) { - for (; i >= 0; i--) { - to = &data->certs[i]; - free(to->subject_t); - free(to->issuer_t); - } - free(data->certs); - free(data->url); - free(data); - warn_user("NoMemory", 0); - return; - } - snprintf(to->version, sizeof data->certs->version, "%ld", - from->version); - snprintf(to->valid_from, sizeof data->certs->valid_from, "%s", - from->not_before); - snprintf(to->type, sizeof data->certs->type, "%d", - from->cert_type); - snprintf(to->valid_to, sizeof data->certs->valid_to, "%s", - from->not_after); - snprintf(to->serial, sizeof data->certs->serial, "%ld", - from->serial); - } - - tree = calloc(sizeof(struct tree), 1); - if (!tree) { - //ro_gui_cert_close(ssl_w); - warn_user("NoMemory", 0); - return; - } - tree->root = tree_create_folder_node(NULL, "Root"); - if (!tree->root) { -// ro_gui_cert_close(ssl_w); - warn_user("NoMemory", 0); - free(tree); - tree = NULL; - return; - } - tree->root->expanded = true; - tree->handle = 0; - tree->movable = false; - tree->no_drag = true; - tree->no_vscroll = true; - tree->no_furniture = true; - tree->single_selection = true; - data->tree = tree; - - /* put the SSL names in the tree */ - for (i = 0; i < (long)num; i++) { - node = tree_create_leaf_node(tree->root, certs[i].subject); - if (node) { - node->data.data = TREE_ELEMENT_SSL; - tree_set_node_sprite(node, "small_xxx", "small_xxx"); - } - } - tree_initialise(tree); - - ami_open_tree(tree,AMI_TREE_SSLCERT); - twin = (struct treeview_window *)data->tree->handle; - - if(yesorno = ASPrintf("%s|%s",messages_get("Accept"),messages_get("Reject"))) - { - if(reqcontents = ASPrintf("%s\n\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s", - messages_get("SSLError"), - messages_get("Subject"), - to->subject_t, - messages_get("Issuer"), - to->issuer_t, - messages_get("Version"), - to->version, - messages_get("ValidFrom"), - to->valid_from, - messages_get("ValidTo"), - to->valid_to)) - { - res = TimedDosRequesterTags(TDR_ImageType,TDRIMAGE_QUESTION, - TDR_Window,twin->win, - TDR_TitleString,messages_get("NetSurf"), - TDR_GadgetString,yesorno, - TDR_FormatString,reqcontents, - TAG_DONE); - - FreeVec(reqcontents); - } - - FreeVec(yesorno); - } - - if(res == 1) - { - ami_gui_cert_apply(data); - } - ami_gui_cert_close(data); - -} - -void ami_gui_cert_close(struct session_data *data) -{ - unsigned long i; - - if(data->tree->handle) - { - ami_tree_close((struct treeview_window *)data->tree->handle); - win_destroyed = true; - } + struct sslcert_session_data *data; + struct treeview_window *ssl_window; - assert(data); + data = sslcert_create_session_data(num, url, cb, cbpw); -/* - for (i = 0; i < data->num; i++) { - if (data->certs[i].subject) - textarea_destroy(data->certs[i].subject); - if (data->certs[i].issuer) - textarea_destroy(data->certs[i].issuer); - } -*/ + ssl_window = ami_tree_create(sslcert_get_tree_flags(), data); + if(!ssl_window) return; - /* Send failure if callback exists */ - if (data->cb != NULL) - data->cb(false, data->cbpw); + sslcert_load_tree(ami_tree_get_tree(ssl_window), certs, data); - if (data->tree) { - tree_delete_node(data->tree, data->tree->root, false); - free(data->tree); - } - - free(data->certs); - free(data->url); - free(data); + ami_tree_open(ssl_window, AMI_TREE_SSLCERT); } -bool ami_gui_cert_apply(struct session_data *session) +void ami_ssl_free(struct treeview_window *twin) { - assert(session); - - urldb_set_cert_permissions(session->url, true); - - session->cb(true, session->cbpw); - - /* Invalidate callback */ - session->cb = NULL; - session->cbpw = NULL; - - return true; + ami_tree_destroy(twin); } diff --git a/windows/hotlist.c b/amiga/sslcert.h index dab18d11c..10078d9bb 100644 --- a/windows/hotlist.c +++ b/amiga/sslcert.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Vincent Sanders <vince@simtec.co.uk> + * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,9 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "desktop/browser.h" -#include "content/hlcache.h" +#ifndef AMIGA_SSLCERT_H +#define AMIGA_SSLCERT_H +#include "desktop/sslcert.h" -void hotlist_visited(hlcache_handle *content) -{ -} +void ami_ssl_free(struct treeview_window *twin); +#endif diff --git a/amiga/theme.c b/amiga/theme.c index cf9148581..7b10e139b 100644 --- a/amiga/theme.c +++ b/amiga/theme.c @@ -121,7 +121,7 @@ void ami_theme_throbber_setup(void) char throbberfile[1024]; Object *dto; - ami_get_theme_filename(throbberfile,"theme_throbber"); + ami_get_theme_filename(throbberfile,"theme_throbber",false); throbber_frames=atoi(messages_get("theme_throbber_frames")); throbber_update_interval = atoi(messages_get("theme_throbber_delay")); if(throbber_update_interval == 0) throbber_update_interval = 100; @@ -170,15 +170,20 @@ void ami_theme_throbber_free(void) p96FreeBitMap(throbber); } -void ami_get_theme_filename(char *filename, char *themestring) +void ami_get_theme_filename(char *filename, char *themestring, bool protocol) { + if(protocol) + strcpy(filename,"file:///"); + else + strcpy(filename,""); + if(messages_get(themestring)[0] == '*') { - strncpy(filename, messages_get(themestring) + 1, 100); + strncat(filename,messages_get(themestring)+1,100); } else { - strcpy(filename, option_theme); + strcat(filename, option_theme); AddPart(filename, messages_get(themestring), 100); } } @@ -273,7 +278,7 @@ void ami_init_mouse_pointers(void) if(option_truecolour_mouse_pointers) { - ami_get_theme_filename(&ptrfname,ptrs32[i]); + ami_get_theme_filename(&ptrfname,ptrs32[i], false); if(dobj = GetIconTags(ptrfname,ICONGETA_UseFriendBitMap,TRUE,TAG_DONE)) { if(IconControl(dobj, ICONCTRLA_GetImageDataFormat, &format, TAG_DONE)) @@ -325,7 +330,7 @@ void ami_init_mouse_pointers(void) if(!mouseptrobj[i]) { - ami_get_theme_filename(ptrfname,ptrs[i]); + ami_get_theme_filename(ptrfname,ptrs[i], false); if(ptrfile = Open(ptrfname,MODE_OLDFILE)) { int mx,my; diff --git a/amiga/theme.h b/amiga/theme.h index 3a396fdb1..ba1295d61 100644 --- a/amiga/theme.h +++ b/amiga/theme.h @@ -27,7 +27,7 @@ ULONG throbber_width, throbber_height; void ami_theme_init(void); -void ami_get_theme_filename(char *filename, char *themestring); +void ami_get_theme_filename(char *filename, char *themestring, bool protocol); void ami_theme_throbber_setup(void); void ami_theme_throbber_free(void); diff --git a/amiga/tree.c b/amiga/tree.c index 7000fb76c..f01805314 100755 --- a/amiga/tree.c +++ b/amiga/tree.c @@ -1,5 +1,5 @@ /* - * Copyright 2008,2009 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,13 +16,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "desktop/tree.h" -#include <proto/listbrowser.h> #include <proto/window.h> #include <proto/layout.h> +#include <proto/space.h> +#include <proto/label.h> +#include <proto/scroller.h> #include <classes/window.h> -#include <gadgets/listbrowser.h> +#include <gadgets/space.h> +#include <images/label.h> #include <gadgets/layout.h> +#include <gadgets/scroller.h> #include <reaction/reaction_macros.h> #include "amiga/gui.h" #include "content/urldb.h" @@ -36,222 +39,395 @@ #include "utils/messages.h" #include <proto/bitmap.h> #include <images/bitmap.h> +#include <proto/graphics.h> +#include <intuition/icclass.h> +#include <proto/asl.h> +#include <proto/utility.h> +#include <libraries/gadtools.h> +#include <proto/dos.h> +#include "amiga/utf8.h" +#include "desktop/cookies.h" +#include "desktop/history_global_core.h" +#include "desktop/hotlist.h" +#include "amiga/sslcert.h" +#include "utils/utils.h" + +#define AMI_TREE_MENU_ITEMS 19 + +struct treeview_window { + struct Window *win; + Object *objects[OID_LAST]; + struct Gadget *gadgets[GID_LAST]; + struct nsObject *node; + ULONG pad[5]; + int type; + struct NewMenu *menu; + char *menu_name[AMI_TREE_MENU_ITEMS]; + struct tree *tree; + struct Hook scrollerhook; + uint32 key_state; + uint32 mouse_state; + int drag_x; + int drag_y; + struct timeval lastclick; + int max_width; + int max_height; + struct gui_globals globals; + struct sslcert_session_data *ssl_data; +}; + +void ami_tree_draw(struct treeview_window *twin); +static void ami_tree_redraw_request(int x, int y, int width, int height, + void *data); +static void ami_tree_resized(struct tree *tree, int width, + int height, void *data); +static void ami_tree_scroll_visible(int y, int height, void *data); +static void ami_tree_get_window_dimensions(int *width, int *height, void *data); + +const struct treeview_table ami_tree_callbacks = { + .redraw_request = ami_tree_redraw_request, + .resized = ami_tree_resized, + .scroll_visible = ami_tree_scroll_visible, + .get_window_dimensions = ami_tree_get_window_dimensions +}; + +struct treeview_window *ami_tree_create(uint8 flags, + struct sslcert_session_data *ssl_data) +{ + struct treeview_window *twin; -struct Node *selectednode; -struct node *selectednode2; + twin = AllocVec(sizeof(struct treeview_window), + MEMF_PRIVATE | MEMF_CLEAR); + + if(!twin) + { + warn_user("NoMemory", 0); + return NULL; + } -void ami_add_elements(struct treeview_window *twin,struct node *root,WORD *gen); -bool ami_tree_launch_node(struct tree *tree, struct node *node); -void free_browserlist(struct List *list); -void ami_move_node(struct treeview_window *twin,int move); -void ami_new_bookmark(struct treeview_window *twin); -void ami_recreate_listbrowser(struct treeview_window *twin); + twin->ssl_data = ssl_data; -void tree_initialise_redraw(struct tree *tree) -{ + twin->tree = tree_create(flags, &ami_tree_callbacks, twin); + return twin; } -void tree_redraw_area(struct tree *tree, int x, int y, int width, int height) +void ami_tree_destroy(struct treeview_window *twin) { + tree_delete(twin->tree); + FreeVec(twin); } -void tree_draw_line(int x, int y, int width, int height) +struct tree *ami_tree_get_tree(struct treeview_window *twin) { + return twin->tree; } -void tree_draw_node_element(struct tree *tree, struct node_element *element) +void ami_tree_resized(struct tree *tree, int width, int height, void *data) { - return; -#if 0 -/* add element to listbrowser list */ + struct treeview_window *twin = data; + struct IBox *bbox; - struct Node *lbnode; - struct treeview_window *twin = tree->handle; - struct node *tempnode; - int generation=1; - BOOL edit = FALSE; + twin->max_height = height; + twin->max_width = width; - tempnode = element->parent; - edit = tempnode->editable; - - while(tempnode) + if(twin->win) { - tempnode = tempnode->parent; - generation++; - } + GetAttr(SPACE_AreaBox,twin->gadgets[GID_BROWSER],(ULONG *)&bbox); - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_SPRITE: - case NODE_ELEMENT_TEXT: - if (lbnode = AllocListBrowserNode(3, -// LBNA_UserData,nodetime, - LBNA_Generation,1, - LBNA_Column, 0, - LBNCA_CopyText,TRUE, - LBNCA_Text, element->text, - LBNCA_Editable,edit, - LBNA_Column, 1, - LBNCA_CopyText,TRUE, - LBNCA_Text, "", - LBNA_Column, 2, - LBNCA_CopyText,TRUE, - LBNCA_Text, "", - TAG_DONE)) - { - AddTail(twin->listbrowser_list, lbnode); - } - break; + RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL, + SCROLLER_Total, height, + SCROLLER_Visible, bbox->Height, + TAG_DONE); + + RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL, + SCROLLER_Total, width, + SCROLLER_Visible, bbox->Width, + TAG_DONE); } -#endif } -void tree_draw_node_expansion(struct tree *tree, struct node *node) +/** + * Retrieves the dimensions of the window with the tree + * + * \param data user data assigned to the tree on tree creation + * \param width will be updated to window width if not NULL + * \param height will be updated to window height if not NULL + */ +void ami_tree_get_window_dimensions(int *width, int *height, void *data) { - DebugPrintF("tree_draw_node_expansion\n"); + struct treeview_window *twin = data; + struct IBox *bbox; + + GetAttr(SPACE_AreaBox,twin->gadgets[GID_BROWSER],(ULONG *)&bbox); + + if(width) *width = bbox->Width; + if(height) *height = bbox->Height; } -void tree_recalculate_node_element(struct node_element *element) +/** + * Translates a content_type to the name of a respective icon + * + * \param content_type content type + * \param buffer buffer for the icon name + */ +void tree_icon_name_from_content_type(char *buffer, content_type type) { + // TODO: design/acquire icons + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_NSSPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + ami_get_theme_filename(buffer,"theme_list_content",true); + break; + } } -void tree_update_URL_node(struct node *node, const char *url, - const struct url_data *data) +/** + * Scrolls the tree to make an element visible + * + * \param y Y coordinate of the element + * \param height height of the element + * \param data user data assigned to the tree on tree creation + */ +void ami_tree_scroll_visible(int y, int height, void *data) { - struct node_element *element; - char buffer[256]; + ULONG sy, scrollset; + struct IBox *bbox; + struct treeview_window *twin = data; - assert(node); + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&sy); + GetAttr(SPACE_AreaBox,twin->gadgets[GID_BROWSER],(ULONG *)&bbox); - element = tree_find_element(node, TREE_ELEMENT_URL); + if((y > sy) && ((y + height) < (sy + bbox->Height))) return; - if (!element) - return; - if (data) { - /* node is linked, update */ - assert(!node->editable); - if (!data->title) - urldb_set_url_title(url, url); - - if (!data->title) - return; - - node->data.text = data->title; - } else { - /* node is not linked, find data */ - assert(node->editable); - data = urldb_get_url_data(element->text); - if (!data) - return; - } + if((y <= sy) || (height > bbox->Height)) scrollset = (ULONG)y; + else scrollset = sy + (y + height) - (sy + bbox->Height); -/* not implemented yet - if (element) { - sprintf(buffer, "small_%.3x", ro_content_filetype_from_type(data->type)); - 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_LAST_VISIT); - if (element) { - snprintf(buffer, 256, (char *)messages_get("TreeLast"), - (data->last_visit > 0) ? - ctime((time_t *)&data->last_visit) : - (char *)messages_get("TreeUnknown")); - if (data->last_visit > 0) - buffer[strlen(buffer) - 1] = '\0'; - free((void *)element->text); - element->text = (char *)strdup(buffer); - } + RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL, + SCROLLER_Top, scrollset, + TAG_DONE); - element = tree_find_element(node, TREE_ELEMENT_VISITS); - if (element) { - snprintf(buffer, 256, (char *)messages_get("TreeVisits"), - data->visits); - free((void *)element->text); - element->text = (char *)strdup(buffer); - } + ami_tree_draw(twin); } -void tree_resized(struct tree *tree) +void ami_tree_scroll(struct treeview_window *twin, int sx, int sy) { + int x, y; + + if(!twin) return; + if(sx<0) sx=0; + if(sy<0) sy=0; + + GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&x); + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&y); + + RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL, + SCROLLER_Top, y + sy, + TAG_DONE); + + RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL, + SCROLLER_Top, x + sx, + TAG_DONE); + + ami_tree_draw(twin); } -void tree_set_node_sprite_folder(struct node *node) + +void ami_tree_scroller_hook(struct Hook *hook,Object *object,struct IntuiMessage *msg) { -} + ULONG gid,x,y; + struct treeview_window *twin = hook->h_Data; + struct IntuiWheelData *wheel; + + switch(msg->Class) + { + case IDCMP_IDCMPUPDATE: + gid = GetTagData( GA_ID, 0, msg->IAddress ); + + switch( gid ) + { + case OID_HSCROLL: + case OID_VSCROLL: + ami_tree_draw(twin); + break; + } + break; + + case IDCMP_EXTENDEDMOUSE: + if(msg->Code == IMSGCODE_INTUIWHEELDATA) + { + wheel = (struct IntuiWheelData *)msg->IAddress; + + ami_tree_scroll(twin, (wheel->WheelX * 20), (wheel->WheelY * 20)); + } + break; + } +} -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded) +void ami_tree_menu(struct treeview_window *twin) { + if(twin->menu) return; + + if(twin->menu = AllocVec(sizeof(struct NewMenu) * AMI_TREE_MENU_ITEMS, MEMF_CLEAR)) + { + twin->menu[0].nm_Type = NM_TITLE; + twin->menu_name[0] = ami_utf8_easy((char *)messages_get("Tree")); + twin->menu[0].nm_Label = twin->menu_name[0]; + + twin->menu[1].nm_Type = NM_ITEM; + twin->menu_name[1] = ami_utf8_easy((char *)messages_get("TreeExport")); + twin->menu[1].nm_Label = twin->menu_name[1]; + if(twin->type == AMI_TREE_COOKIES) + twin->menu[1].nm_Flags = NM_ITEMDISABLED; + twin->menu[1].nm_CommKey = "S"; + + twin->menu[2].nm_Type = NM_ITEM; + twin->menu[2].nm_Label = NM_BARLABEL; + + twin->menu[3].nm_Type = NM_ITEM; + twin->menu_name[3] = ami_utf8_easy((char *)messages_get("Expand")); + twin->menu[3].nm_Label = twin->menu_name[3]; + + twin->menu[4].nm_Type = NM_SUB; + twin->menu_name[4] = ami_utf8_easy((char *)messages_get("All")); + twin->menu[4].nm_Label = twin->menu_name[4]; + twin->menu[4].nm_CommKey = "+"; + + if(twin->type == AMI_TREE_COOKIES) + { + twin->menu_name[5] = ami_utf8_easy((char *)messages_get("Domains")); + twin->menu_name[6] = ami_utf8_easy((char *)messages_get("Cookies")); + } + else + { + twin->menu_name[5] = ami_utf8_easy((char *)messages_get("Folders")); + twin->menu_name[6] = ami_utf8_easy((char *)messages_get("Links")); + } + + twin->menu[5].nm_Type = NM_SUB; + twin->menu[5].nm_Label = twin->menu_name[5]; // tree-specific title + + twin->menu[6].nm_Type = NM_SUB; + twin->menu[6].nm_Label = twin->menu_name[6]; // tree-specific title + + twin->menu[7].nm_Type = NM_ITEM; + twin->menu_name[7] = ami_utf8_easy((char *)messages_get("Collapse")); + twin->menu[7].nm_Label = twin->menu_name[7]; + + twin->menu[8].nm_Type = NM_SUB; + twin->menu[8].nm_Label = twin->menu_name[4]; + twin->menu[8].nm_CommKey = "-"; + + twin->menu[9].nm_Type = NM_SUB; + twin->menu[9].nm_Label = twin->menu_name[5]; // tree-specific title + + twin->menu[10].nm_Type = NM_SUB; + twin->menu[10].nm_Label = twin->menu_name[6]; // tree-specific title + + twin->menu[11].nm_Type = NM_ITEM; + twin->menu[11].nm_Label = NM_BARLABEL; + + twin->menu[12].nm_Type = NM_ITEM; + twin->menu_name[12] = ami_utf8_easy((char *)messages_get("CloseWindow")); + twin->menu[12].nm_Label = twin->menu_name[12]; + twin->menu[12].nm_CommKey = "K"; + + twin->menu[13].nm_Type = NM_TITLE; + twin->menu_name[13] = ami_utf8_easy((char *)messages_get("Edit")); + twin->menu[13].nm_Label = twin->menu_name[13]; + + twin->menu[14].nm_Type = NM_ITEM; + twin->menu_name[14] = ami_utf8_easy((char *)messages_get("TreeDelete")); + twin->menu[14].nm_Label = twin->menu_name[14]; + twin->menu[14].nm_CommKey = "D"; + + twin->menu[15].nm_Type = NM_ITEM; + twin->menu[15].nm_Label = NM_BARLABEL; + + twin->menu[16].nm_Type = NM_ITEM; + twin->menu_name[16] = ami_utf8_easy((char *)messages_get("SelectAllNS")); + twin->menu[16].nm_Label = twin->menu_name[16]; + twin->menu[16].nm_CommKey = "A"; + + twin->menu[17].nm_Type = NM_ITEM; + twin->menu_name[17] = ami_utf8_easy((char *)messages_get("ClearNS")); + twin->menu[17].nm_Label = twin->menu_name[17]; + twin->menu[17].nm_CommKey = "Z"; + + twin->menu[18].nm_Type = NM_END; + } } -void ami_open_tree(struct tree *tree,int type) +void ami_tree_open(struct treeview_window *twin,int type) { - struct treeview_window *twin; BOOL msel = TRUE,nothl = TRUE,launchdisable=FALSE; static WORD gen=0; char *wintitle; char folderclosed[100],folderopen[100],item[100]; - if(tree->handle) + if(twin->win) { - twin = (struct treeview_window *)tree->handle; WindowToFront(twin->win); ActivateWindow(twin->win); return; } - twin = AllocVec(sizeof(struct treeview_window),MEMF_PRIVATE | MEMF_CLEAR); - twin->listbrowser_list = AllocVec(sizeof(struct List),MEMF_PRIVATE | MEMF_CLEAR); + twin->type = type; - static struct ColumnInfo columninfo[] = - { - { 80,"Name", CIF_DRAGGABLE | CIF_SORTABLE}, - { 20,"URL", CIF_DRAGGABLE }, -// { 5,"Visits", CIF_DRAGGABLE }, - { -1, (STRPTR)~0, -1 } - }; - - if(tree->single_selection) msel = FALSE; - - ami_get_theme_filename(&folderclosed,"theme_list_folder_closed"); - ami_get_theme_filename(&folderopen,"theme_list_folder_open"); - - switch(type) + switch(twin->type) { case AMI_TREE_HOTLIST: nothl = FALSE; wintitle = (char *)messages_get("Hotlist"); - ami_get_theme_filename(&item,"theme_list_bookmark"); break; case AMI_TREE_COOKIES: nothl = TRUE; launchdisable=TRUE; wintitle = (char *)messages_get("Cookies"); - ami_get_theme_filename(&item,"theme_list_cookie"); break; case AMI_TREE_HISTORY: nothl = TRUE; wintitle = (char *)messages_get("GlobalHistory"); - ami_get_theme_filename(&item,"theme_list_history"); break; case AMI_TREE_SSLCERT: nothl = TRUE; wintitle = (char *)messages_get("SSLCerts"); - ami_get_theme_filename(&item,"theme_list_sslcert"); break; } - NewList(twin->listbrowser_list); + twin->scrollerhook.h_Entry = (void *)ami_tree_scroller_hook; + twin->scrollerhook.h_Data = twin; - tree->handle = (void *)twin; - twin->tree = tree; - ami_add_elements(twin,twin->tree->root,&gen); + ami_init_layers(&twin->globals, scrn->Width, scrn->Height); + ami_tree_menu(twin); - twin->objects[OID_MAIN] = WindowObject, + if(type == AMI_TREE_SSLCERT) + { + twin->objects[OID_MAIN] = WindowObject, WA_ScreenTitle,nsscreentitle, WA_Title,wintitle, WA_Activate, TRUE, @@ -259,43 +435,77 @@ void ami_open_tree(struct tree *tree,int type) WA_DragBar, TRUE, WA_CloseGadget, TRUE, WA_SizeGadget, TRUE, + WA_Height, scrn->Height / 2, WA_CustomScreen,scrn, + WA_ReportMouse,TRUE, + WA_IDCMP,IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE | + IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE | + IDCMP_EXTENDEDMOUSE, + WINDOW_HorizProp,1, + WINDOW_VertProp,1, + WINDOW_IDCMPHook,&twin->scrollerhook, + WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE, WINDOW_SharedPort,sport, WINDOW_UserData,twin, - WINDOW_IconifyGadget, TRUE, + /* WINDOW_NewMenu, twin->menu, -> No menu for SSL Cert */ + WINDOW_IconifyGadget, FALSE, WINDOW_Position, WPOS_CENTERSCREEN, WINDOW_ParentGroup, twin->gadgets[GID_MAIN] = VGroupObject, - LAYOUT_AddChild, twin->gadgets[GID_TREEBROWSER] = ListBrowserObject, - GA_ID, GID_TREEBROWSER, - GA_RelVerify, TRUE, - GA_ReadOnly,FALSE, - LISTBROWSER_ColumnInfo, &columninfo, -// LISTBROWSER_ColumnTitles, TRUE, - LISTBROWSER_Hierarchical,TRUE, - LISTBROWSER_Editable,TRUE, -// LISTBROWSER_TitleClickable,TRUE, - LISTBROWSER_AutoFit, TRUE, - LISTBROWSER_HorizontalProp, TRUE, - LISTBROWSER_Labels, twin->listbrowser_list, -// LISTBROWSER_MultiSelect,msel, - LISTBROWSER_ShowSelected,TRUE, - LISTBROWSER_ShowImage,BitMapObject, - BITMAP_SourceFile,folderclosed, - BITMAP_Screen,scrn, - BITMAP_Masking,TRUE, - BitMapEnd, - LISTBROWSER_HideImage,BitMapObject, - BITMAP_SourceFile,folderopen, - BITMAP_Screen,scrn, - BITMAP_Masking,TRUE, - BitMapEnd, - LISTBROWSER_LeafImage,BitMapObject, - BITMAP_SourceFile,item, - BITMAP_Screen,scrn, - BITMAP_Masking,TRUE, - BitMapEnd, - ListBrowserEnd, - CHILD_NominalSize,TRUE, + LAYOUT_AddImage, LabelObject, + LABEL_Text, messages_get("SSLError"), + LabelEnd, + LAYOUT_AddChild, twin->gadgets[GID_BROWSER] = SpaceObject, + GA_ID, GID_BROWSER, + SPACE_Transparent,TRUE, + SPACE_BevelStyle, BVS_DISPLAY, + SpaceEnd, + LAYOUT_AddChild, HGroupObject, + LAYOUT_AddChild, twin->gadgets[GID_OPEN] = ButtonObject, + GA_ID,GID_OPEN, + GA_Text,messages_get("Accept"), + GA_RelVerify,TRUE, + ButtonEnd, + LAYOUT_AddChild, twin->gadgets[GID_CANCEL] = ButtonObject, + GA_ID,GID_CANCEL, + GA_Text,messages_get("Reject"), + GA_RelVerify,TRUE, + ButtonEnd, + EndGroup, + CHILD_WeightedHeight,0, + EndGroup, + EndWindow; + } + else + { + twin->objects[OID_MAIN] = WindowObject, + WA_ScreenTitle,nsscreentitle, + WA_Title,wintitle, + WA_Activate, TRUE, + WA_DepthGadget, TRUE, + WA_DragBar, TRUE, + WA_CloseGadget, TRUE, + WA_SizeGadget, TRUE, + WA_Height, scrn->Height / 2, + WA_CustomScreen,scrn, + WA_ReportMouse,TRUE, + WA_IDCMP,IDCMP_MOUSEMOVE | IDCMP_MOUSEBUTTONS | IDCMP_NEWSIZE | + IDCMP_RAWKEY | IDCMP_GADGETUP | IDCMP_IDCMPUPDATE | + IDCMP_EXTENDEDMOUSE, + WINDOW_HorizProp,1, + WINDOW_VertProp,1, + WINDOW_IDCMPHook,&twin->scrollerhook, + WINDOW_IDCMPHookBits,IDCMP_IDCMPUPDATE | IDCMP_EXTENDEDMOUSE, + WINDOW_SharedPort,sport, + WINDOW_UserData,twin, + WINDOW_NewMenu, twin->menu, + WINDOW_IconifyGadget, FALSE, + WINDOW_Position, WPOS_CENTERSCREEN, + WINDOW_ParentGroup, twin->gadgets[GID_MAIN] = VGroupObject, + LAYOUT_AddChild, twin->gadgets[GID_BROWSER] = SpaceObject, + GA_ID, GID_BROWSER, + SPACE_Transparent,TRUE, + SPACE_BevelStyle, BVS_DISPLAY, + SpaceEnd, LAYOUT_AddChild, HGroupObject, LAYOUT_AddChild, twin->gadgets[GID_OPEN] = ButtonObject, GA_ID,GID_OPEN, @@ -315,24 +525,6 @@ void ami_open_tree(struct tree *tree,int type) GA_RelVerify,TRUE, GA_Disabled,nothl, ButtonEnd, - LAYOUT_AddChild, twin->gadgets[GID_LEFT] = ButtonObject, - GA_ID,GID_LEFT, - BUTTON_AutoButton,BAG_LFARROW, - GA_RelVerify,TRUE, - GA_Disabled,nothl, //(!tree->movable), - ButtonEnd, - LAYOUT_AddChild, twin->gadgets[GID_UP] = ButtonObject, - GA_ID,GID_UP, - BUTTON_AutoButton,BAG_UPARROW, - GA_RelVerify,TRUE, - GA_Disabled,nothl, //(!tree->movable), - ButtonEnd, - LAYOUT_AddChild, twin->gadgets[GID_DOWN] = ButtonObject, - GA_ID,GID_DOWN, - BUTTON_AutoButton,BAG_DNARROW, - GA_RelVerify,TRUE, - GA_Disabled,nothl, //(!tree->movable), - ButtonEnd, LAYOUT_AddChild, twin->gadgets[GID_DEL] = ButtonObject, GA_ID,GID_DEL, GA_Text,messages_get("TreeDelete"), @@ -342,161 +534,89 @@ void ami_open_tree(struct tree *tree,int type) CHILD_WeightedHeight,0, EndGroup, EndWindow; + } twin->win = (struct Window *)RA_OpenWindow(twin->objects[OID_MAIN]); - twin->node = AddObject(window_list,AMINS_TVWINDOW); - twin->node->objstruct = twin; -} - -/** - * Launches a node using all known methods. - * - * \param node the node to launch - * \return whether the node could be launched - */ -bool ami_tree_launch_node(struct tree *tree, struct node *node) -{ - struct node_element *element; + GetAttr(WINDOW_HorizObject, twin->objects[OID_MAIN], + (ULONG *)&twin->objects[OID_HSCROLL]); + GetAttr(WINDOW_VertObject, twin->objects[OID_MAIN], + (ULONG *)&twin->objects[OID_VSCROLL]); - assert(node); + RefreshSetGadgetAttrs((APTR)twin->objects[OID_VSCROLL], twin->win, NULL, + GA_ID,OID_VSCROLL, + ICA_TARGET,ICTARGET_IDCMP, + TAG_DONE); - element = tree_find_element(node, TREE_ELEMENT_URL); - if (element) { - browser_window_create(element->text, NULL, 0, true, false); - return true; - } + RefreshSetGadgetAttrs((APTR)twin->objects[OID_HSCROLL], twin->win, NULL, + GA_ID,OID_HSCROLL, + ICA_TARGET,ICTARGET_IDCMP, + TAG_DONE); -/* not implemented yet - element = tree_find_element(node, TREE_ELEMENT_SSL); - if (element) { - ro_gui_cert_open(tree, node); - return true; - } -*/ + twin->node = AddObject(window_list,AMINS_TVWINDOW); + twin->node->objstruct = twin; - return false; + ami_tree_resized(twin->tree, twin->max_width, twin->max_height, twin); + tree_set_redraw(twin->tree, true); + ami_tree_draw(twin); } void ami_tree_close(struct treeview_window *twin) { - twin->tree->handle = 0; - DisposeObject(twin->objects[OID_MAIN]); - FreeListBrowserList(twin->listbrowser_list); - FreeVec(twin->listbrowser_list); - //free_browserlist(twin->listbrowser_list); - DelObject(twin->node); -} - -void free_browserlist(struct List *list) -{ - struct Node *node, *nextnode; - - if(IsListEmpty(list)) return; + int i; - node = GetHead(list); + tree_set_redraw(twin->tree, false); + twin->win = NULL; + DisposeObject(twin->objects[OID_MAIN]); + DelObjectNoFree(twin->node); + ami_free_layers(&twin->globals); - do - { - nextnode = GetSucc(node); -// FreeVec(node->ln_Name); - FreeListBrowserNode(node); - } while(node = nextnode); + for(i=0;i<AMI_TREE_MENU_ITEMS;i++) + { + if(twin->menu_name[i] && (twin->menu_name[i] != NM_BARLABEL)) ami_utf8_free(twin->menu_name[i]); + } + FreeVec(twin->menu); + twin->menu = NULL; + if(twin->type == AMI_TREE_SSLCERT) ami_ssl_free(twin); } -void ami_add_elements(struct treeview_window *twin,struct node *root,WORD *gen) +void ami_tree_update_quals(struct treeview_window *twin) { - struct Node *lbnode; - struct tree *tree = twin->tree; - struct node *tempnode; - int generation=1; - BOOL edit = FALSE; - struct node_element *element=NULL,*element2=NULL,*element3=NULL; - struct node *node; - ULONG flags = 0; - STRPTR text1 = "",text2 = "",text3 = ""; - - *gen = *gen + 1; - for (node = root; node; node = node->next) - { - element = tree_find_element(node, TREE_ELEMENT_NAME); - if(!element) element = tree_find_element(node, TREE_ELEMENT_TITLE); - if(!element) element = tree_find_element(node, TREE_ELEMENT_SSL); - if(element && element->text) - { - text1 = (char *)element->text; - } + uint32 quals = 0; -/* Really, the second column needs axing - relevant data should appear in an -area below the listview when items are selected */ + GetAttr(WINDOW_Qualifier, twin->objects[OID_MAIN], (uint32 *)&quals); - element2 = tree_find_element(node, TREE_ELEMENT_URL); - if(!element2) element2 = tree_find_element(node, TREE_ELEMENT_VALUE); - if(!element2) element2 = tree_find_element(node, TREE_ELEMENT_COMMENT); + twin->key_state = 0; - if(element2 && element2->text) - { - text2 = (char *)element2->text; - } - else - { - text2 = ""; - } + if((quals & IEQUALIFIER_LSHIFT) || (quals & IEQUALIFIER_RSHIFT)) + { + twin->key_state |= BROWSER_MOUSE_MOD_1; + } -// element = tree_find_element(node, TREE_ELEMENT_VISITS); - - flags = 0; - /*if(node->expanded) */ flags = LBFLG_SHOWCHILDREN; - if(node->folder) flags |= LBFLG_HASCHILDREN; - if(!node->parent) flags |= LBFLG_HIDDEN; - - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_SPRITE: - case NODE_ELEMENT_TEXT: - if (lbnode = AllocListBrowserNode(3, - LBNA_UserData,node, - LBNA_Generation,*gen - 1, - LBNA_Selected,node->selected, - LBNA_Flags,flags, - LBNA_Column, 0, - LBNCA_CopyText,TRUE, - LBNCA_MaxChars,256, - LBNCA_Text, text1, - LBNCA_Editable,node->editable, - LBNA_Column, 1, - LBNCA_CopyText,TRUE, - LBNCA_MaxChars,256, - LBNCA_Text, text2, - LBNCA_Editable,FALSE, - TAG_DONE)) - { - AddTail(twin->listbrowser_list, lbnode); - if(node == selectednode2) selectednode = lbnode; - } - break; - } + if(quals & IEQUALIFIER_CONTROL) + { + twin->key_state |= BROWSER_MOUSE_MOD_2; + } - if (node->child) - { - ami_add_elements(twin,node->child,gen); - } + if((quals & IEQUALIFIER_LALT) || (quals & IEQUALIFIER_RALT)) + { + twin->key_state |= BROWSER_MOUSE_MOD_3; } - *gen = *gen - 1; } BOOL ami_tree_event(struct treeview_window *twin) { /* return TRUE if window destroyed */ - ULONG class,result,relevent = 0; - ULONG column; + ULONG class,result,storage = 0; uint16 code; struct MenuItem *item; - struct node *treenode; - struct Node *lbnode; - struct node_element *element; - char *text; -// ULONG editcols[] = {TREE_ELEMENT_TITLE,TREE_ELEMENT_URL}; - static WORD gen=0; + ULONG menunum=0,itemnum=0,subnum=0; + int xs, ys, x, y; + struct IBox *bbox; + struct timeval curtime; + struct InputEvent *ie; + int nskey; + char fname[1024]; while((result = RA_HandleInput(twin->objects[OID_MAIN],&code)) != WMHI_LASTMSG) { @@ -505,126 +625,386 @@ BOOL ami_tree_event(struct treeview_window *twin) case WMHI_GADGETUP: switch(result & WMHI_GADGETMASK) { - case GID_TREEBROWSER: - GetAttrs(twin->gadgets[GID_TREEBROWSER], - LISTBROWSER_RelEvent,&relevent, - TAG_DONE); - - switch(relevent) + case GID_OPEN: + if(twin->type == AMI_TREE_SSLCERT) { - case LBRE_DOUBLECLICK: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - ami_tree_launch_node(twin->tree,treenode); - break; - - case LBRE_EDIT: - GetAttrs(twin->gadgets[GID_TREEBROWSER], - LISTBROWSER_SelectedNode,(ULONG *)&lbnode, -// LISTBROWSER_RelColumn,(ULONG *)&column, - TAG_DONE); - - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - - element = tree_find_element(treenode,TREE_ELEMENT_TITLE); - GetListBrowserNodeAttrs(lbnode, - LBNA_Column,column, - LBNCA_Text,(ULONG *)&text, - TAG_DONE); - element->text = (char *)strdup(text); - tree_handle_node_element_changed(twin->tree, element); - break; - - case LBRE_HIDECHILDREN: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - tree_set_node_expanded(twin->tree, treenode, false); - break; - - case LBRE_SHOWCHILDREN: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - tree_set_node_expanded(twin->tree, treenode, true); - break; + sslcert_accept(twin->ssl_data); + ami_tree_close(twin); + return TRUE; } + else tree_launch_selected(twin->tree); break; - case GID_OPEN: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - ami_tree_launch_node(twin->tree,treenode); + case GID_CANCEL: + sslcert_reject(twin->ssl_data); + ami_tree_close(twin); + return TRUE; break; case GID_NEWF: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - if(lbnode) - { - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - } - else - { - treenode = twin->tree->root; - } - - tree_create_folder_node(treenode,(char *)messages_get("TreeNewFolder")); - - ami_recreate_listbrowser(twin); + hotlist_add_folder(); break; case GID_NEWB: - ami_new_bookmark(twin); + hotlist_add_entry(); break; - case GID_UP: - ami_move_node(twin,AMI_MOVE_UP); + case GID_DEL: + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_delete_selected(); + break; + case AMI_TREE_COOKIES: + cookies_delete_selected(); + break; + case AMI_TREE_HOTLIST: + hotlist_delete_selected(); + break; + } break; + } + break; - case GID_DOWN: - ami_move_node(twin,AMI_MOVE_DOWN); - break; + case WMHI_MOUSEMOVE: + GetAttr(SPACE_AreaBox, twin->gadgets[GID_BROWSER], (ULONG *)&bbox); - case GID_LEFT: - ami_move_node(twin,AMI_MOVE_OUT); - break; + GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&xs); + x = twin->win->MouseX - bbox->Left + xs; - case GID_DEL: - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - tree_delete_node(twin->tree, treenode, false); -/* We are recreating the list from scratch as there is no obvious easy way - to delete children from a listbrowser list */ - ami_recreate_listbrowser(twin); + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&ys); + y = twin->win->MouseY - bbox->Top + ys; + + if((x >= xs) && (y >= ys) && (x < bbox->Width + xs) && + (y < bbox->Height + ys)) + { + ami_tree_update_quals(twin); + + if(twin->mouse_state & BROWSER_MOUSE_PRESS_1) + { + tree_mouse_action(twin->tree, + BROWSER_MOUSE_DRAG_1 | twin->key_state, x, y); + twin->mouse_state = BROWSER_MOUSE_HOLDING_1 | + BROWSER_MOUSE_DRAG_ON; + if(twin->drag_x == 0) twin->drag_x = x; + if(twin->drag_y == 0) twin->drag_y = y; + + } + else if(twin->mouse_state & BROWSER_MOUSE_PRESS_2) + { + tree_mouse_action(twin->tree, + BROWSER_MOUSE_DRAG_2 | twin->key_state, x, y); + twin->mouse_state = BROWSER_MOUSE_HOLDING_2 | + BROWSER_MOUSE_DRAG_ON; + if(twin->drag_x == 0) twin->drag_x = x; + if(twin->drag_y == 0) twin->drag_y = y; + } + else + { + tree_mouse_action(twin->tree, + twin->mouse_state | twin->key_state, x, y); + } + } + twin->lastclick.tv_sec = 0; + twin->lastclick.tv_usec = 0; + break; + + case WMHI_MOUSEBUTTONS: + GetAttr(SPACE_AreaBox, twin->gadgets[GID_BROWSER], (ULONG *)&bbox); + GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&xs); + x = twin->win->MouseX - bbox->Left + xs; + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&ys); + y = twin->win->MouseY - bbox->Top + ys; + + ami_tree_update_quals(twin); + + if((x >= xs) && (y >= ys) && (x < bbox->Width + xs) && + (y < bbox->Height + ys)) + { + switch(code) + { + case SELECTDOWN: + twin->mouse_state = BROWSER_MOUSE_PRESS_1; + break; + case MIDDLEDOWN: + twin->mouse_state = BROWSER_MOUSE_PRESS_2; + break; + } + tree_mouse_action(twin->tree, + twin->mouse_state | twin->key_state, x, y); + } + + if(x < xs) x = xs; + if(y < ys) y = ys; + if(x >= bbox->Width + xs) x = bbox->Width + xs - 1; + if(y >= bbox->Height + ys) y = bbox->Height + ys - 1; + + switch(code) + { + case SELECTUP: + if(twin->mouse_state & BROWSER_MOUSE_PRESS_1) + { + CurrentTime(&curtime.tv_sec,&curtime.tv_usec); + + twin->mouse_state = BROWSER_MOUSE_CLICK_1; + + if(twin->lastclick.tv_sec) + { + if(DoubleClick(twin->lastclick.tv_sec, + twin->lastclick.tv_usec, + curtime.tv_sec, curtime.tv_usec)) + twin->mouse_state |= BROWSER_MOUSE_DOUBLE_CLICK; + } + tree_mouse_action(twin->tree, + twin->mouse_state | twin->key_state, x, y); + + if(twin->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) + { + twin->lastclick.tv_sec = 0; + twin->lastclick.tv_usec = 0; + } + else + { + twin->lastclick.tv_sec = curtime.tv_sec; + twin->lastclick.tv_usec = curtime.tv_usec; + } + } + else + { + tree_drag_end(twin->tree, twin->mouse_state, + twin->drag_x, twin->drag_y, x, y); + } + twin->mouse_state=0; + twin->drag_x = 0; + twin->drag_y = 0; + break; + case MIDDLEUP: + if(twin->mouse_state & BROWSER_MOUSE_PRESS_2) + { + tree_mouse_action(twin->tree, + BROWSER_MOUSE_CLICK_2 | twin->key_state, x, y); + } + else + { + tree_drag_end(twin->tree, twin->mouse_state, + twin->drag_x, twin->drag_y, x, y); + } + twin->mouse_state=0; + twin->drag_x = 0; + twin->drag_y = 0; break; } break; -/* no menus yet, copied in as will probably need it later + case WMHI_RAWKEY: + storage = result & WMHI_GADGETMASK; + + GetAttr(WINDOW_InputEvent,twin->objects[OID_MAIN],(ULONG *)&ie); + nskey = ami_key_to_nskey(storage, ie); + tree_keypress(twin->tree, nskey); + break; + case WMHI_MENUPICK: - item = ItemAddress(gwin->win->MenuStrip,code); + item = ItemAddress(twin->win->MenuStrip,code); while (code != MENUNULL) { - ami_menupick(code,gwin); + menunum = MENUNUM(code); + itemnum = ITEMNUM(code); + subnum = SUBNUM(code); + + switch(menunum) + { + case 0: // tree + switch(itemnum) + { + case 0: // export + if(AslRequestTags(savereq, + ASLFR_TitleText,messages_get("NetSurf"), + ASLFR_Screen,scrn, + ASLFR_InitialFile,"tree_export.html", + TAG_DONE)) + { + strlcpy(&fname,savereq->fr_Drawer,1024); + AddPart(fname,savereq->fr_File,1024); + ami_update_pointer(twin->win,GUI_POINTER_WAIT); + if(twin->type == AMI_TREE_HISTORY) + history_global_export(fname); + else if(twin->type == AMI_TREE_HOTLIST) + hotlist_export(fname); + ami_update_pointer(twin->win,GUI_POINTER_DEFAULT); + } + break; + + case 2: // expand + switch(subnum) + { + case 0: // all + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_expand_all(); + break; + case AMI_TREE_COOKIES: + cookies_expand_all(); + break; + case AMI_TREE_HOTLIST: + hotlist_expand_all(); + break; + } + break; + + case 1: // lev 1 + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_expand_directories(); + break; + case AMI_TREE_COOKIES: + cookies_expand_domains(); + break; + case AMI_TREE_HOTLIST: + hotlist_expand_directories(); + break; + } + break; + + case 2: // lev 2 + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_expand_addresses(); + break; + case AMI_TREE_COOKIES: + cookies_expand_cookies(); + break; + case AMI_TREE_HOTLIST: + hotlist_expand_addresses(); + break; + } + break; + } + break; + + case 3: // collapse + switch(subnum) + { + case 0: // all + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_collapse_all(); + break; + case AMI_TREE_COOKIES: + cookies_collapse_all(); + break; + case AMI_TREE_HOTLIST: + hotlist_collapse_all(); + break; + } + break; + + case 1: // lev 1 + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_collapse_directories(); + break; + case AMI_TREE_COOKIES: + cookies_collapse_domains(); + break; + case AMI_TREE_HOTLIST: + hotlist_collapse_directories(); + break; + } + break; + + case 2: // lev 2 + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_collapse_addresses(); + break; + case AMI_TREE_COOKIES: + cookies_collapse_cookies(); + break; + case AMI_TREE_HOTLIST: + hotlist_collapse_addresses(); + break; + } + break; + } + break; + + case 5: // close + ami_tree_close(twin); + return TRUE; + break; + } + break; + + case 1: // edit + switch(itemnum) + { + case 0: // delete + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_delete_selected(); + break; + case AMI_TREE_COOKIES: + cookies_delete_selected(); + break; + case AMI_TREE_HOTLIST: + hotlist_delete_selected(); + break; + } + break; + + case 2: // select all + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_select_all(); + break; + case AMI_TREE_COOKIES: + cookies_select_all(); + break; + case AMI_TREE_HOTLIST: + hotlist_select_all(); + break; + } + break; + + case 3: // clear + switch(twin->type) + { + case AMI_TREE_HISTORY: + history_global_clear_selection(); + break; + case AMI_TREE_COOKIES: + cookies_clear_selection(); + break; + case AMI_TREE_HOTLIST: + hotlist_clear_selection(); + break; + } + break; + } + break; + } + if(win_destroyed) break; code = item->NextSelect; } break; -*/ + + case WMHI_NEWSIZE: + ami_tree_draw(twin); + break; case WMHI_CLOSEWINDOW: + if(twin->type == AMI_TREE_SSLCERT) + sslcert_reject(twin->ssl_data); ami_tree_close(twin); return TRUE; break; @@ -633,103 +1013,44 @@ BOOL ami_tree_event(struct treeview_window *twin) return FALSE; } -void ami_move_node(struct treeview_window *twin,int move) +void ami_tree_draw(struct treeview_window *twin) { - struct Node *lbnode = NULL; - struct node *treenode,*moveto; - bool before; - - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - - if(!lbnode) return; - - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, -// for multiselects? LBNA_Selected,(BOOL *)&sel, - TAG_DONE); - - selectednode2 = treenode; + struct IBox *bbox; + int x, y; - tree_set_node_selected(twin->tree,twin->tree->root,false); - tree_set_node_selected(twin->tree,treenode,true); - - switch(move) - { - case AMI_MOVE_UP: - moveto = treenode->previous; - before = true; - break; - - case AMI_MOVE_DOWN: - moveto = treenode->next; - before = false; - break; - - case AMI_MOVE_OUT: - moveto = treenode->parent->previous; - before = false; - break; - } + if(!twin) return; - if(!moveto) return; + GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&x); + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&y); + GetAttr(SPACE_AreaBox,twin->gadgets[GID_BROWSER],(ULONG *)&bbox); - tree_delink_node(treenode); - //tree_move_selected_nodes(twin->tree,moveto,before); - tree_link_node(moveto,treenode,before); - ami_recreate_listbrowser(twin); - selectednode2 = NULL; + ami_tree_redraw_request(x, y, bbox->Width, bbox->Height, twin); } -void ami_new_bookmark(struct treeview_window *twin) +void ami_tree_redraw_request(int x, int y, int width, int height, void *data) { - const struct url_data *data; - struct Node *lbnode; - struct node *treenode; - char *url,*title; + struct treeview_window *twin = data; + struct IBox *bbox; + int pos_x, pos_y; - GetAttr(LISTBROWSER_SelectedNode,twin->gadgets[GID_TREEBROWSER],(ULONG *)&lbnode); - if(lbnode) - { - GetListBrowserNodeAttrs(lbnode, - LBNA_UserData,(ULONG *)&treenode, - TAG_DONE); - } - else - { - treenode = twin->tree->root; - } + if(!twin->win) return; +// if(tree_get_redraw(twin->tree) == false) return; - url = (char *)strdup("http://www.netsurf-browser.org"); + ami_update_pointer(twin->win, GUI_POINTER_WAIT); + glob = &twin->globals; - data = urldb_get_url_data(url); - if (!data) - { - urldb_add_url(url); - urldb_set_url_persistence(url,true); - data = urldb_get_url_data(url); - } + GetAttr(SPACE_AreaBox,twin->gadgets[GID_BROWSER],(ULONG *)&bbox); + GetAttr(SCROLLER_Top, twin->objects[OID_HSCROLL], (ULONG *)&pos_x); + GetAttr(SCROLLER_Top, twin->objects[OID_VSCROLL], (ULONG *)&pos_y); - if (data) - { - title = data->title; - tree_create_URL_node(treenode,url,data,title); - ami_recreate_listbrowser(twin); - } -} - -void ami_recreate_listbrowser(struct treeview_window *twin) -{ - static WORD gen=0; + if(x - pos_x + width > bbox->Width) width = bbox->Width - (x - pos_x); + if(y - pos_y + height > bbox->Height) height = bbox->Height - (y - pos_y); - SetGadgetAttrs(twin->gadgets[GID_TREEBROWSER],twin->win,NULL, - LISTBROWSER_Labels,~0, - TAG_DONE); + tree_draw(twin->tree, -pos_x, -pos_y, x, y, width, height); - FreeListBrowserList(twin->listbrowser_list); - ami_add_elements(twin,twin->tree->root,&gen); + BltBitMapRastPort(twin->globals.bm, x - pos_x, y - pos_y, twin->win->RPort, + bbox->Left + x - pos_x, bbox->Top + y - pos_y, width, height, 0x0C0); - RefreshSetGadgetAttrs(twin->gadgets[GID_TREEBROWSER],twin->win,NULL, - LISTBROWSER_Labels,twin->listbrowser_list, - LISTBROWSER_SelectedNode,selectednode, - TAG_DONE); + ami_update_pointer(twin->win, GUI_POINTER_DEFAULT); + glob = &browserglob; } diff --git a/amiga/tree.h b/amiga/tree.h index 4129df1d2..315938d0d 100755 --- a/amiga/tree.h +++ b/amiga/tree.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Chris Young <chris@unsatisfactorysoftware.co.uk> + * Copyright 2008, 2009 Chris Young <chris@unsatisfactorysoftware.co.uk> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -22,15 +22,10 @@ #include <exec/types.h> #include <intuition/classusr.h> #include "amiga/gui.h" +#include "desktop/tree.h" +#include "desktop/sslcert.h" -struct treeview_window { - struct nsObject *node; - struct Window *win; - Object *objects[OID_LAST]; - struct Gadget *gadgets[GID_LAST]; - struct tree *tree; - struct List *listbrowser_list; -}; +struct treeview_window; enum { @@ -40,15 +35,15 @@ enum AMI_TREE_SSLCERT }; -enum -{ - AMI_MOVE_UP, - AMI_MOVE_DOWN, - AMI_MOVE_OUT -}; +struct treeview_window *ami_tree_create(uint8 flags, + struct sslcert_session_data *ssl_data); +void ami_tree_destroy(struct treeview_window *twin); +struct tree *ami_tree_get_tree(struct treeview_window *twin); -void ami_open_tree(struct tree *tree,int type); +void ami_tree_open(struct treeview_window *twin,int type); void ami_tree_close(struct treeview_window *twin); BOOL ami_tree_event(struct treeview_window *twin); -void ami_recreate_listbrowser(struct treeview_window *twin); + +extern const struct treeview_table ami_tree_callbacks; + #endif diff --git a/beos/beos_bitmap.cpp b/beos/beos_bitmap.cpp index 881ffe727..5df1fce38 100644 --- a/beos/beos_bitmap.cpp +++ b/beos/beos_bitmap.cpp @@ -312,6 +312,16 @@ void bitmap_set_suspendable(void *vbitmap, void *private_word, struct bitmap *bitmap = (struct bitmap *)vbitmap; } +int bitmap_get_width(void *vbitmap){ + struct bitmap *bitmap = (struct bitmap *)vbitmap; + return bitmap->primary->Bounds().Width() + 1; +} + +int bitmap_get_height(void *vbitmap){ + struct bitmap *bitmap = (struct bitmap *)vbitmap; + return bitmap->primary->Bounds().Height() + 1; +} + static BBitmap * nsbeos_bitmap_generate_pretile(BBitmap *primary, int repeat_x, int repeat_y) { diff --git a/beos/beos_gui.cpp b/beos/beos_gui.cpp index d8cb4bbb2..c06b0c7fc 100644 --- a/beos/beos_gui.cpp +++ b/beos/beos_gui.cpp @@ -74,7 +74,6 @@ extern "C" { #include "beos/beos_window.h" #include "beos/options.h" #include "beos/beos_throbber.h" -#include "beos/beos_history.h" #include "beos/beos_filetype.h" //#include "beos/beos_download.h" #include "beos/beos_schedule.h" @@ -641,7 +640,6 @@ void gui_init(int argc, char** argv) urldb_load(option_url_file); urldb_load_cookies(option_cookie_file); - nsbeos_history_init(); //nsbeos_download_initialise(); if (!replicated) @@ -1059,11 +1057,6 @@ void die(const char * const error) exit(EXIT_FAILURE); } - -void hotlist_visited(hlcache_handle *content) -{ -} - void gui_cert_verify(const char *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw) diff --git a/beos/beos_history.cpp b/beos/beos_history.cpp deleted file mode 100644 index b35d6d075..000000000 --- a/beos/beos_history.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2008 François Revol <mmu_man@users.sourceforge.net> - * Copyright 2006 Rob Kendrick <rjek@rjek.com> - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define __STDBOOL_H__ 1 -extern "C" { -#include "utils/log.h" -#include "content/urldb.h" -} - -#include "beos/beos_history.h" -#include "beos/beos_gui.h" -#include "beos/beos_window.h" - -#include <View.h> -#include <Window.h> - - -enum -{ - COL_TITLE = 0, - COL_ADDRESS, - COL_LASTVISIT, - COL_TOTALVISITS, - COL_THUMBNAIL, - COL_NCOLS -}; - -BWindow *wndHistory; -#warning XXX -#if 0 /* GTK */ -static GtkTreeView *treeview; -static GtkTreeStore *history_tree; -static GtkTreeSelection *selection; - -static bool nsgtk_history_add_internal(const char *, const struct url_data *); -static void nsgtk_history_selection_changed(GtkTreeSelection *, gpointer); -#endif - -void nsbeos_history_init(void) -{ -#warning XXX -#if 0 /* GTK */ - GtkCellRenderer *renderer; - - wndHistory = GTK_WINDOW(glade_xml_get_widget(gladeWindows, - "wndHistory")); - treeview = GTK_TREE_VIEW(glade_xml_get_widget(gladeWindows, - "treeHistory")); - history_tree = gtk_tree_store_new(COL_NCOLS, - G_TYPE_STRING, /* title */ - G_TYPE_STRING, /* address */ - G_TYPE_STRING, /* last visit */ - G_TYPE_INT, /* nr. visits */ - GDK_TYPE_PIXBUF); /* thumbnail */ - - selection = gtk_tree_view_get_selection(treeview); - gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE); - g_signal_connect(G_OBJECT(selection), "changed", - G_CALLBACK(nsgtk_history_selection_changed), NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(treeview, -1, "Title", - renderer, - "text", - COL_TITLE, - NULL); - - gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(history_tree)); - -#endif - nsbeos_history_update(); -} - -void nsbeos_history_update(void) -{ -#warning XXX -#if 0 /* GTK */ - gtk_tree_store_clear(history_tree); - urldb_iterate_entries(nsgtk_history_add_internal); -#endif -} - -bool nsbeos_history_add_internal(const char *url, const struct url_data *data) -{ -#warning XXX -#if 0 /* GTK */ - GtkTreeIter iter; - - if (data->visits > 0) - { - gtk_tree_store_append(history_tree, &iter, NULL); - gtk_tree_store_set(history_tree, &iter, - COL_TITLE, data->title, - COL_ADDRESS, url, - COL_LASTVISIT, "Unknown", - COL_TOTALVISITS, data->visits, - -1); - } - -#endif - return true; -} - -#warning XXX -#if 0 /* GTK */ -void nsgtk_history_selection_changed(GtkTreeSelection *treesel, gpointer g) -{ - GtkTreeIter iter; - GtkTreeModel *model = GTK_TREE_MODEL(history_tree); - if (gtk_tree_selection_get_selected(treesel, &model, &iter)) - { - gchar *b; - gint i; - char buf[20]; - - gtk_tree_model_get(model, &iter, COL_ADDRESS, &b, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeWindows, - "labelHistoryAddress")), b); - - gtk_tree_model_get(model, &iter, COL_LASTVISIT, &b, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeWindows, - "labelHistoryLastVisit")), b); - - gtk_tree_model_get(model, &iter, COL_TOTALVISITS, - &i, -1); - snprintf(buf, 20, "%d", i); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeWindows, - "labelHistoryVisits")), buf); - - - - } - else - { - - } -} - -void nsgtk_history_row_activated(GtkTreeView *tv, GtkTreePath *path, - GtkTreeViewColumn *column, gpointer g) -{ - GtkTreeModel *model; - GtkTreeIter iter; - - model = gtk_tree_view_get_model(tv); - if (gtk_tree_model_get_iter(model, &iter, path)) - { - gchar *b; - - gtk_tree_model_get(model, &iter, COL_ADDRESS, &b, -1); - - browser_window_create((const char *)b, NULL, NULL, true); - } -} -#endif - -void global_history_add(const char *url) -{ - const struct url_data *data; - - data = urldb_get_url_data(url); - if (!data) - return; - - nsbeos_history_add_internal(url, data); - -} diff --git a/beos/beos_scaffolding.cpp b/beos/beos_scaffolding.cpp index a5cc832a7..3fb147f1f 100644 --- a/beos/beos_scaffolding.cpp +++ b/beos/beos_scaffolding.cpp @@ -63,7 +63,6 @@ extern "C" { #include "beos/beos_options.h" //#include "beos/beos_completion.h" #include "beos/beos_throbber.h" -//#include "beos/beos_history.h" #include "beos/beos_window.h" //#include "beos/beos_schedule.h" //#include "beos/beos_download.h" diff --git a/beos/beos_treeview.cpp b/beos/beos_treeview.cpp index 1688a95fa..6763c25b8 100644 --- a/beos/beos_treeview.cpp +++ b/beos/beos_treeview.cpp @@ -26,107 +26,59 @@ extern "C" { #include "utils/config.h" #include "desktop/tree.h" +#include "desktop/tree_url_node.h" } +const char tree_directory_icon_name[] = "directory.png"; +const char tree_content_icon_name[] = "content.png"; -/** - * 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 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(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) { -} - -/** - * 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) { -} - -/** - * Sets a node element as having a folder sprite - * - * \param node the node to update - */ -void tree_set_node_sprite_folder(struct node *node) { - -} - -/** - * 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, const char *url, - const struct url_data *data) { -} /** - * Updates the tree owner following a tree resize + * Translates a content_type to the name of a respective icon * - * \param tree the tree to update the owner of + * \param content_type content type + * \param buffer buffer for the icon name */ -void tree_resized(struct tree *tree) { +void tree_icon_name_from_content_type(char *buffer, content_type type) +{ + // TODO: design/acquire icons + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_SPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_DRAW + case CONTENT_DRAW: +#endif +#ifdef WITH_ARTWORKS + case CONTENT_ARTWORKS: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + sprintf(buffer, tree_content_icon_name); + break; + } } diff --git a/content/urldb.c b/content/urldb.c index e4a163738..ee550503f 100644 --- a/content/urldb.c +++ b/content/urldb.c @@ -245,13 +245,11 @@ static bool urldb_iterate_partial_path(const struct path_data *parent, static bool urldb_iterate_entries_host(struct search_node *parent, bool (*url_callback)(const char *url, const struct url_data *data), - bool (*cookie_callback)(const char *domain, - const struct cookie_data *data)); + bool (*cookie_callback)(const struct cookie_data *data)); static bool urldb_iterate_entries_path(const struct path_data *parent, bool (*url_callback)(const char *url, const struct url_data *data), - bool (*cookie_callback)(const char *domain, - const struct cookie_data *data)); + bool (*cookie_callback)(const struct cookie_data *data)); /* Insertion */ static struct host_part *urldb_add_host_node(const char *part, @@ -1390,8 +1388,7 @@ void urldb_iterate_entries(bool (*callback)(const char *url, * * \param callback Function to callback for each entry */ -void urldb_iterate_cookies(bool (*callback)(const char *domain, - const struct cookie_data *data)) +void urldb_iterate_cookies(bool (*callback)(const struct cookie_data *data)) { int i; @@ -1415,8 +1412,7 @@ void urldb_iterate_cookies(bool (*callback)(const char *domain, bool urldb_iterate_entries_host(struct search_node *parent, bool (*url_callback)(const char *url, const struct url_data *data), - bool (*cookie_callback)(const char *domain, - const struct cookie_data *data)) + bool (*cookie_callback)(const struct cookie_data *data)) { if (parent == &empty) return true; @@ -1452,11 +1448,11 @@ bool urldb_iterate_entries_host(struct search_node *parent, bool urldb_iterate_entries_path(const struct path_data *parent, bool (*url_callback)(const char *url, const struct url_data *data), - bool (*cookie_callback)(const char *domain, - const struct cookie_data *data)) + bool (*cookie_callback)(const struct cookie_data *data)) { const struct path_data *p = parent; - + const struct cookie_data *c; + do { if (p->children != NULL) { /* Drill down into children */ @@ -1478,11 +1474,10 @@ bool urldb_iterate_entries_path(const struct path_data *parent, (const struct url_data *) u)) return false; } else { - if (p->cookies && !cookie_callback( - p->cookies->domain, - (const struct cookie_data *) - p->cookies)) - return false; + c = (const struct cookie_data *)p->cookies; + for (; c != NULL; c = c->next) + if (!cookie_callback(c)) + return false; } /* Now, find next node to process. */ @@ -2447,9 +2442,7 @@ char *urldb_get_cookie(const char *url) url_func_result res; int i; - assert(url); - -// LOG(("%s", url)); + assert(url != NULL); urldb_add_url(url); @@ -2529,14 +2522,11 @@ char *urldb_get_cookie(const char *url) version = c->version; c->last_used = now; - cookies_update(c->domain, - (struct cookie_data *)c); + cookies_schedule_update((struct cookie_data *)c); } } } -// LOG(("%s", ret)); - /* Now consider cookies whose paths prefix-match ours */ for (p = p->parent; p; p = p->parent) { /* Find directory's path entry(ies) */ @@ -2546,7 +2536,6 @@ char *urldb_get_cookie(const char *url) continue; for (c = q->cookies; c; c = c->next) { -// LOG(("%p: %s=%s", c, c->name, c->value)); if (c->expires != 1 && c->expires < now) /* cookie has expired => ignore */ continue; @@ -2565,8 +2554,7 @@ char *urldb_get_cookie(const char *url) version = c->version; c->last_used = now; - cookies_update(c->domain, - (struct cookie_data *)c); + cookies_schedule_update((struct cookie_data *)c); } } @@ -2606,13 +2594,11 @@ char *urldb_get_cookie(const char *url) version = c->version; c->last_used = now; - cookies_update(c->domain, (struct cookie_data *)c); + cookies_schedule_update((struct cookie_data *)c); } } -// LOG(("%s", ret)); - /* Finally consider domain cookies for hosts which domain match ours */ for (h = (const struct host_part *)p; h && h != &db_root; h = h->parent) { @@ -2638,12 +2624,10 @@ char *urldb_get_cookie(const char *url) version = c->version; c->last_used = now; - cookies_update(c->domain, (struct cookie_data *)c); + cookies_schedule_update((struct cookie_data *)c); } } -// LOG(("%s", ret)); - if (count == 0) { /* No cookies found */ free(path); @@ -2712,8 +2696,6 @@ bool urldb_set_cookie(const char *header, const char *url, assert(url && header); -// LOG(("'%s' : '%s'", url, header)); - /* strip fragment */ urlt = strdup(url); if (!urlt) @@ -2928,7 +2910,7 @@ bool urldb_set_cookie(const char *header, const char *url, /* Now insert into database */ if (!urldb_insert_cookie(c, scheme, urlt)) goto error; - cookies_update(c->domain, (struct cookie_data *)c); + cookies_schedule_update((struct cookie_data *)c); } while (cur < end); free(host); @@ -3378,6 +3360,8 @@ bool urldb_insert_cookie(struct cookie_internal_data *c, const char *scheme, d->prev->next = d->next; else p->cookies = d->next; + + cookies_remove((struct cookie_data *)d); urldb_free_cookie(d); urldb_free_cookie(c); } else { @@ -3392,8 +3376,11 @@ bool urldb_insert_cookie(struct cookie_internal_data *c, const char *scheme, c->prev->next = c; else p->cookies = c; + + cookies_remove((struct cookie_data *)d); urldb_free_cookie(d); -// LOG(("%p: %s=%s", c, c->name, c->value)); + + cookies_schedule_update((struct cookie_data *)c); } } else { c->prev = p->cookies_end; @@ -3403,7 +3390,6 @@ bool urldb_insert_cookie(struct cookie_internal_data *c, const char *scheme, else p->cookies = c; p->cookies_end = c; -// LOG(("%p: %s=%s", c, c->name, c->value)); } return true; @@ -3771,9 +3757,7 @@ void urldb_delete_cookie_paths(const char *domain, const char *path, else p->cookies_end = c->prev; - if (p->cookies == NULL) - cookies_update(domain, NULL); - + cookies_remove((struct cookie_data *)c); urldb_free_cookie(c); return; diff --git a/content/urldb.h b/content/urldb.h index 5080dc52d..bbf378332 100644 --- a/content/urldb.h +++ b/content/urldb.h @@ -108,8 +108,7 @@ void urldb_iterate_partial(const char *prefix, /* Iteration */ void urldb_iterate_entries(bool (*callback)(const char *url, const struct url_data *data)); -void urldb_iterate_cookies(bool (*callback)(const char *domain, - const struct cookie_data *cookie)); +void urldb_iterate_cookies(bool (*callback)(const struct cookie_data *cookie)); /* Debug */ void urldb_dump(void); diff --git a/desktop/browser.c b/desktop/browser.c index 8c53813a6..b301b5fa3 100644 --- a/desktop/browser.c +++ b/desktop/browser.c @@ -46,6 +46,7 @@ #include "desktop/download.h" #include "desktop/frames.h" #include "desktop/history_core.h" +#include "desktop/hotlist.h" #include "desktop/gui.h" #include "desktop/options.h" #include "desktop/selection.h" diff --git a/desktop/cookies.c b/desktop/cookies.c new file mode 100644 index 000000000..c5dac5101 --- /dev/null +++ b/desktop/cookies.c @@ -0,0 +1,531 @@ +/* + * Copyright 2006 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Cookies (implementation). + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/cookies.h" +#include "desktop/options.h" +#include "desktop/tree.h" +#include "utils/messages.h" +#include "utils/log.h" +#include "utils/url.h" +#include "utils/utils.h" + +/** Flags for each type of cookie tree node. */ +enum tree_element_cookie { + TREE_ELEMENT_PERSISTENT = 0x01, + TREE_ELEMENT_VERSION = 0x02, + TREE_ELEMENT_SECURE = 0x03, + TREE_ELEMENT_LAST_USED = 0x04, + TREE_ELEMENT_EXPIRES = 0x05, + TREE_ELEMENT_PATH = 0x06, + TREE_ELEMENT_DOMAIN = 0x07, + TREE_ELEMENT_COMMENT = 0x08, + TREE_ELEMENT_VALUE = 0x09, +}; + +static struct tree *cookies_tree; +static struct node *cookies_tree_root; +static bool user_delete; +static hlcache_handle *folder_icon; +static hlcache_handle *cookie_icon; + + +/** + * Find an entry in the cookie tree + * + * \param node the node to check the children of + * \param title The title to find + * \return Pointer to node, or NULL if not found + */ +static struct node *cookies_find(struct node *node, const char *title) +{ + struct node *search; + struct node_element *element; + + for (search = tree_node_get_child(node); search; + search = tree_node_get_next(search)) { + element = tree_node_find_element(search, TREE_ELEMENT_TITLE, + NULL); + if (strcmp(title, tree_node_element_get_text(element)) == 0) + return search; + } + return NULL; +} + +/** + * Callback for all cookie tree nodes. + */ +static node_callback_resp cookies_node_callback(void *user_data, struct node_msg_data *msg_data) +{ + struct node *node = msg_data->node; + struct node_element *domain, *path; + const char *domain_t, *path_t, *name_t; + char *space; + bool is_folder = tree_node_is_folder(node); + + /* we don't remove any icons here */ + if (msg_data->msg == NODE_DELETE_ELEMENT_IMG) + return NODE_CALLBACK_HANDLED; + + /* let the tree handle events other than text data removal */ + if (msg_data->msg != NODE_DELETE_ELEMENT_TXT) + return NODE_CALLBACK_NOT_HANDLED; + + /* check if it's a domain folder */ + if (is_folder) + return NODE_CALLBACK_NOT_HANDLED; + + switch (msg_data->flag) { + case TREE_ELEMENT_TITLE: + if (!user_delete) + break; + /* get the rest of the cookie data */ + domain = tree_node_find_element(node, + TREE_ELEMENT_DOMAIN, NULL); + path = tree_node_find_element(node, TREE_ELEMENT_PATH, + NULL); + + if ((domain != NULL) && + (path != NULL)) { + domain_t = tree_node_element_get_text(domain) + + strlen(messages_get( + "TreeDomain")) - 4; + space = strchr(domain_t, ' '); + if (space != NULL) + *space = '\0'; + path_t = tree_node_element_get_text(path) + + strlen(messages_get("TreePath")) + - 4; + space = strchr(path_t, ' '); + if (space != NULL) + *space = '\0'; + name_t = msg_data->data.text; + urldb_delete_cookie(domain_t, path_t, name_t); + } + break; + default: + break; + } + + free(msg_data->data.text); + + return NODE_CALLBACK_HANDLED; +} + + +/** + * Updates a tree entry for a cookie. + * + * All information is copied from the cookie_data, and as such can + * be edited and should be freed. + * + * \param node The node to update + * \param data The cookie data to use + * \return true if node updated, or false for failure + */ +static bool cookies_update_cookie_node(struct node *node, + const struct cookie_data *data) +{ + struct node_element *element; + char buffer[32]; + + assert(data != NULL); + + /* update the value text */ + element = tree_node_find_element(node, TREE_ELEMENT_VALUE, NULL); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeValue", + data->value != NULL ? + data->value : + messages_get("TreeUnused"))); + + + /* update the comment text */ + if ((data->comment != NULL) && + (strcmp(data->comment, "") != 0)) { + element = tree_node_find_element(node, TREE_ELEMENT_COMMENT, NULL); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeComment", + data->comment)); + } + + /* update domain text */ + element = tree_node_find_element(node, TREE_ELEMENT_DOMAIN, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeDomain", + data->domain, + data->domain_from_set ? + messages_get("TreeHeaders") : + "")); + + /* update path text */ + element = tree_node_find_element(node, TREE_ELEMENT_PATH, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreePath", data->path, + data->path_from_set ? + messages_get("TreeHeaders") : + "")); + + /* update expiry text */ + element = tree_node_find_element(node, TREE_ELEMENT_EXPIRES, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeExpires", + (data->expires > 0) + ? (data->expires == 1) + ? messages_get("TreeSession") + : ctime(&data->expires) + : messages_get("TreeUnknown"))); + + /* update last used text */ + element = tree_node_find_element(node, TREE_ELEMENT_LAST_USED, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeLastUsed", + (data->last_used > 0) ? + ctime(&data->last_used) : + messages_get("TreeUnknown"))); + + /* update secure text */ + element = tree_node_find_element(node, TREE_ELEMENT_SECURE, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeSecure", + data->secure ? + messages_get("Yes") : + messages_get("No"))); + + /* update version text */ + element = tree_node_find_element(node, TREE_ELEMENT_VERSION, element); + snprintf(buffer, sizeof(buffer), "TreeVersion%i", data->version); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreeVersion", + messages_get(buffer))); + + /* update persistant text */ + element = tree_node_find_element(node, TREE_ELEMENT_PERSISTENT, element); + tree_update_element_text(cookies_tree, + element, + messages_get_buff("TreePersistent", + data->no_destroy ? + messages_get("Yes") : + messages_get("No"))); + + return node; +} + +/** + * Creates an empty tree entry for a cookie, and links it into the tree. + * + * All information is copied from the cookie_data, and as such can + * be edited and should be freed. + * + * \param parent the node to link to + * \param data the cookie data to use + * \return the node created, or NULL for failure + */ +static struct node *cookies_create_cookie_node(struct node *parent, + const struct cookie_data *data) +{ + struct node *node; + char *name; + + name = strdup(data->name); + if (name == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + + node = tree_create_leaf_node(cookies_tree, NULL, name, + false, false, false); + if (node == NULL) { + free(name); + return NULL; + } + + tree_set_node_user_callback(node, cookies_node_callback, NULL); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_PERSISTENT, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_VERSION, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SECURE, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_LAST_USED, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_EXPIRES, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_PATH, false); + + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_DOMAIN, false); + + if ((data->comment) && (strcmp(data->comment, ""))) + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_COMMENT, false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_VALUE, false); + tree_set_node_icon(cookies_tree, node, cookie_icon); + + if (!cookies_update_cookie_node(node, data)) + { + tree_delete_node(NULL, node, false); + return NULL; + } + + tree_link_node(cookies_tree, parent, node, false); + return node; +} + + +/** + * Called when scheduled event gets fired. Actually performs the update. + */ +static void cookies_schedule_callback(void *scheduled_data) +{ + const struct cookie_data *data = scheduled_data; + struct node *node = NULL; + struct node *cookie_node = NULL; + char *domain_cp; + + assert(data != NULL); + + node = cookies_find(cookies_tree_root, data->domain); + + if (node == NULL) { + domain_cp = strdup(data->domain); + if (domain_cp == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return; + } + node = tree_create_folder_node(cookies_tree, + cookies_tree_root, domain_cp, + false, false, false); + if (node != NULL) { + tree_set_node_user_callback(node, cookies_node_callback, + NULL); + tree_set_node_icon(cookies_tree, node, folder_icon); + } + } + + if (node == NULL) + return; + + cookie_node = cookies_find(node, data->name); + if (cookie_node == NULL) + cookies_create_cookie_node(node, data); + else + cookies_update_cookie_node(cookie_node, data); + + return; +} + +/** + * Initialises cookies tree. + * + * \param data user data for the callbacks + * \param start_redraw callback function called before every redraw + * \param end_redraw callback function called after every redraw + * \return true on success, false on memory exhaustion + */ +bool cookies_initialise(struct tree *tree) +{ + + if (tree == NULL) + return false; + + folder_icon = tree_load_icon(tree_directory_icon_name); + cookie_icon = tree_load_icon(tree_content_icon_name); + + /* Create an empty tree */ + cookies_tree = tree; + cookies_tree_root = tree_get_root(cookies_tree); + + user_delete = false; + urldb_iterate_cookies(cookies_schedule_update); + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, true); + + return true; +} + + +/** + * Get flags with which the cookies tree should be created; + * + * \return the flags + */ +unsigned int cookies_get_tree_flags(void) +{ + return TREE_DELETE_EMPTY_DIRS; +} + + +/* exported interface documented in cookies.h */ +bool cookies_schedule_update(const struct cookie_data *data) +{ + assert(data != NULL); + assert(user_delete == false); + + schedule(100, cookies_schedule_callback, (void *)data); + + return true; +} + + +/* exported interface documented in cookies.h */ +void cookies_remove(const struct cookie_data *data) +{ + assert(data != NULL); + + schedule_remove(cookies_schedule_callback, (void *)data); +} + + +/** + * Free memory and release all other resources. + */ +void cookies_cleanup(void) +{ +} + +/* Actions to be connected to front end specific toolbars */ + +/** + * Delete nodes which are currently selected. + */ +void cookies_delete_selected(void) +{ + user_delete = true; + tree_delete_selected_nodes(cookies_tree, cookies_tree_root); + user_delete = false; +} + +/** + * Delete all nodes. + */ +void cookies_delete_all(void) +{ + bool needs_redraw = tree_get_redraw(cookies_tree); + if (needs_redraw) + tree_set_redraw(cookies_tree, false); + + user_delete = true; + tree_set_node_selected(cookies_tree, cookies_tree_root, true, true); + tree_delete_selected_nodes(cookies_tree, cookies_tree_root); + user_delete = false; + + if (needs_redraw) + tree_set_redraw(cookies_tree, true); +} + +/** + * Select all nodes in the tree. + */ +void cookies_select_all(void) +{ + tree_set_node_selected(cookies_tree, cookies_tree_root, true, true); +} + +/** + * Unselect all nodes. + */ +void cookies_clear_selection(void) +{ + tree_set_node_selected(cookies_tree, cookies_tree_root, true, false); +} + +/** + * Expand both domain and cookie nodes. + */ +void cookies_expand_all(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, true); +} + +/** + * Expand domain nodes only. + */ +void cookies_expand_domains(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, true, false); +} + +/** + * Expand cookie nodes only. + */ +void cookies_expand_cookies(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + true, false, true); +} + +/** + * Collapse both domain and cookie nodes. + */ +void cookies_collapse_all(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, true, true); +} + +/** + * Collapse domain nodes only. + */ +void cookies_collapse_domains(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, true, false); +} + +/** + * Collapse cookie nodes only. + */ +void cookies_collapse_cookies(void) +{ + tree_set_node_expanded(cookies_tree, cookies_tree_root, + false, false, true); +} diff --git a/desktop/cookies.h b/desktop/cookies.h index 4311957df..06278c006 100644 --- a/desktop/cookies.h +++ b/desktop/cookies.h @@ -25,8 +25,42 @@ #include <stdbool.h> +#include "desktop/tree.h" + struct cookie_data; -bool cookies_update(const char *domain, const struct cookie_data *data); +bool cookies_initialise(struct tree *tree); +unsigned int cookies_get_tree_flags(void); + +/** + * Perform cookie updates and addition. The update is only scheduled here. + * The actual update is performed in the callback function. + * + * \param data Data of cookie being updated. + * \return true (for urldb_iterate_entries) + */ +bool cookies_schedule_update(const struct cookie_data *data); + +/** + * Remove a cookie from the active set. + * The cookie is to be removed from the active set and no futher + * references made to the cookie data. + * + * \param data Data of cookie being removed. + */ +void cookies_remove(const struct cookie_data *data); + +void cookies_cleanup(void); + +void cookies_delete_selected(void); +void cookies_delete_all(void); +void cookies_select_all(void); +void cookies_clear_selection(void); +void cookies_expand_all(void); +void cookies_expand_domains(void); +void cookies_expand_cookies(void); +void cookies_collapse_all(void); +void cookies_collapse_domains(void); +void cookies_collapse_cookies(void); #endif diff --git a/desktop/history_global_core.c b/desktop/history_global_core.c new file mode 100644 index 000000000..b8cd9a5b0 --- /dev/null +++ b/desktop/history_global_core.c @@ -0,0 +1,464 @@ +/* + * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <stdlib.h> + +#include "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/history_global_core.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "desktop/tree_url_node.h" + +#ifdef riscos +#include "riscos/gui.h" +#endif +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/log.h" + +#define MAXIMUM_BASE_NODES 16 +#define GLOBAL_HISTORY_RECENT_URLS 16 +#define URL_CHUNK_LENGTH 512 + +static struct node *global_history_base_node[MAXIMUM_BASE_NODES]; +static int global_history_base_node_time[MAXIMUM_BASE_NODES]; +static int global_history_base_node_count = 0; + +static bool global_history_initialised; + +static struct tree *global_history_tree; +static struct node *global_history_tree_root; + +static hlcache_handle *folder_icon; + +static const char *const weekday_msg_name [] = +{ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +}; + +/** + * Find an entry in the global history + * + * \param url The URL to find + * \return Pointer to node, or NULL if not found + */ +static struct node *history_global_find(const char *url) +{ + int i; + struct node *node; + const char *text; + + for (i = 0; i < global_history_base_node_count; i++) { + if (!tree_node_is_deleted(global_history_base_node[i])) { + node = tree_node_get_child(global_history_base_node[i]); + for (; node != NULL; node = tree_node_get_next(node)) { + text = tree_url_node_get_url(node); + if ((text != NULL) && !strcmp(url, text)) + return node; + } + } + } + return NULL; +} + +/** + * Internal routine to actually perform global history addition + * + * \param url The URL to add + * \param data URL data associated with URL + * \return true (for urldb_iterate_entries) + */ +static bool global_history_add_internal(const char *url, + const struct url_data *data) +{ + int i, j; + struct node *parent = NULL; + struct node *link; + struct node *node; + bool before = false; + int visit_date; + + assert((url != NULL) && (data != NULL)); + + visit_date = data->last_visit; + + /* find parent node */ + for (i = 0; i < global_history_base_node_count; i++) { + if (global_history_base_node_time[i] <= visit_date) { + parent = global_history_base_node[i]; + break; + } + } + + /* the entry is too old to care about */ + if (parent == NULL) + return true; + + if (tree_node_is_deleted(parent)) { + /* parent was deleted, so find place to insert it */ + link = global_history_tree_root; + + for (j = global_history_base_node_count - 1; j >= 0; j--) { + if (!tree_node_is_deleted(global_history_base_node[j]) && + global_history_base_node_time[j] > + global_history_base_node_time[i]) { + link = global_history_base_node[j]; + before = true; + break; + } + } + + tree_set_node_selected(global_history_tree, + parent, true, false); + tree_set_node_expanded(global_history_tree, + parent, false, true, true); + tree_link_node(global_history_tree, link, parent, before); + } + + /* find any previous occurance */ + if (global_history_initialised == false) { + node = history_global_find(url); + if (node != NULL) { + tree_update_URL_node(global_history_tree, + node, url, data, true); + tree_delink_node(global_history_tree, node); + tree_link_node(global_history_tree, parent, node, + false); + return true; + } + } + + /* Add the node at the bottom */ + node = tree_create_URL_node_shared(global_history_tree, + parent, url, data, + tree_url_node_callback, NULL); + + return true; +} + +static node_callback_resp +history_global_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + if (msg_data->msg == NODE_DELETE_ELEMENT_IMG) + return NODE_CALLBACK_HANDLED; + return NODE_CALLBACK_NOT_HANDLED; +} + +/** + * Initialises a single grouping node for the global history tree. + * + * \return false on memory exhaustion, true otherwise + */ +static bool history_global_initialise_node(const char *title, + time_t base, int days_back) +{ + struct tm *full_time; + char *buffer; + struct node *node; + + base += days_back * 60 * 60 * 24; + if (title == NULL) { + full_time = localtime(&base); + buffer = strdup(messages_get(weekday_msg_name[full_time->tm_wday])); + } else { + buffer = strdup(title); + } + + if (buffer == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + + node = tree_create_folder_node(NULL, NULL, buffer, + false, true, true); + if (node == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + free(buffer); + return false; + } + if (folder_icon != NULL) + tree_set_node_icon(global_history_tree, node, folder_icon); + tree_set_node_user_callback(node, history_global_node_callback, NULL); + + global_history_base_node[global_history_base_node_count] = node; + global_history_base_node_time[global_history_base_node_count] = base; + global_history_base_node_count++; + + return true; +} + +/** + * Initialises the grouping nodes(Today, Yesterday etc.) for the global history + * tree. + * + * \return false on memory exhaustion, true otherwise + */ +static bool history_global_initialise_nodes(void) +{ + struct tm *full_time; + time_t t; + int weekday; + int i; + + /* get the current time */ + t = time(NULL); + if (t == -1) { + LOG(("time info unaviable")); + return false; + } + + /* get the time at the start of today */ + full_time = localtime(&t); + weekday = full_time->tm_wday; + full_time->tm_sec = 0; + full_time->tm_min = 0; + full_time->tm_hour = 0; + t = mktime(full_time); + if (t == -1) { + LOG(("mktime failed")); + return false; + } + + history_global_initialise_node(messages_get("DateToday"), t, 0); + if (weekday > 0) + if (!history_global_initialise_node( + messages_get("DateYesterday"), t, -1)) + return false; + for (i = 2; i <= weekday; i++) + if (!history_global_initialise_node(NULL, t, -i)) + return false; + + if (!history_global_initialise_node(messages_get("Date1Week"), + t, -weekday - 7)) + return false; + if (!history_global_initialise_node(messages_get("Date2Week"), + t, -weekday - 14)) + return false; + if (!history_global_initialise_node(messages_get("Date3Week"), + t, -weekday - 21)) + return false; + + return true; +} + +/** + * Initialises the global history tree. + * + * \param data user data for the callbacks + * \param start_redraw callback function called before every redraw + * \param end_redraw callback function called after every redraw + * \return true on success, false on memory exhaustion + */ +bool history_global_initialise(struct tree *tree) +{ + struct node *first; + + folder_icon = tree_load_icon(tree_directory_icon_name); + tree_url_node_init(); + + if (tree == NULL) + return false; + + global_history_tree = tree; + global_history_tree_root = tree_get_root(global_history_tree); + + if (!history_global_initialise_nodes()) + return false; + + global_history_initialised = true; + urldb_iterate_entries(global_history_add_internal); + global_history_initialised = false; + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, true); + first = tree_node_get_child(global_history_tree_root); + if (first != NULL) + tree_set_node_expanded(global_history_tree, first, + true, false, false); + + return true; +} + + +/** + * Get flags with which the global history tree should be created; + * + * \return the flags + */ +unsigned int history_global_get_tree_flags(void) +{ + return TREE_NO_FLAGS; +} + + +/** + * Deletes the global history tree. + */ +void history_global_cleanup(void) +{ +} + + +/** + * Adds a url to the global history. + * + * \param url the url to be added + */ +void global_history_add(const char *url) +{ + const struct url_data *data; + + data = urldb_get_url_data(url); + if (data == NULL) + return; + + global_history_add_internal(url, data); +} + + +/* Actions to be connected to front end specific toolbars */ + +/** + * Save the global history in a human-readable form under the given location. + * + * \param path the path where the history will be saved + */ +bool history_global_export(const char *path) +{ + return tree_urlfile_save(global_history_tree, path, "NetSurf history"); +} + +/** + * Delete nodes which are currently selected. + */ +void history_global_delete_selected(void) +{ + tree_delete_selected_nodes(global_history_tree, + global_history_tree_root); +} + +/** + * Delete all nodes. + */ +void history_global_delete_all(void) +{ + bool redraw_needed = tree_get_redraw(global_history_tree); + if (redraw_needed) + tree_set_redraw(global_history_tree, false); + + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, true); + tree_delete_selected_nodes(global_history_tree, + global_history_tree_root); + + if (redraw_needed) + tree_set_redraw(global_history_tree, true); +} + +/** + * Select all nodes in the tree. + */ +void history_global_select_all(void) +{ + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, true); +} + +/** + * Unselect all nodes. + */ +void history_global_clear_selection(void) +{ + tree_set_node_selected(global_history_tree, global_history_tree_root, + true, false); +} + +/** + * Expand grouping folders and history entries. + */ +void history_global_expand_all(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, true, true); +} + +/** + * Expand grouping folders only. + */ +void history_global_expand_directories(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, true, false); +} + +/** + * Expand history entries only. + */ +void history_global_expand_addresses(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + true, false, true); +} + +/** + * Collapse grouping folders and history entries. + */ +void history_global_collapse_all(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, true); +} + +/** + * Collapse grouping folders only. + */ +void history_global_collapse_directories(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, true, false); +} + +/** + * Collapse history entries only. + */ +void history_global_collapse_addresses(void) +{ + tree_set_node_expanded(global_history_tree, global_history_tree_root, + false, false, true); +} + +/** + * Open the selected entries in seperate browser windows. + */ +void history_global_launch_selected(void) +{ + tree_launch_selected(global_history_tree); +} diff --git a/desktop/history_global_core.h b/desktop/history_global_core.h new file mode 100644 index 000000000..97c578f3d --- /dev/null +++ b/desktop/history_global_core.h @@ -0,0 +1,44 @@ +/* + * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _NETSURF_DESKTOP_HISTORY_GLOBAL_H_ +#define _NETSURF_DESKTOP_HISTORY_GLOBAL_H_ + +#include <stdbool.h> + +#include "desktop/tree.h" + +bool history_global_initialise(struct tree *tree); +unsigned int history_global_get_tree_flags(void); +void history_global_cleanup(void); + +bool history_global_export(const char *path); +void history_global_delete_selected(void); +void history_global_delete_all(void); +void history_global_select_all(void); +void history_global_clear_selection(void); +void history_global_expand_all(void); +void history_global_expand_directories(void); +void history_global_expand_addresses(void); +void history_global_collapse_all(void); +void history_global_collapse_directories(void); +void history_global_collapse_addresses(void); +void history_global_launch_selected(void); + +#endif diff --git a/desktop/hotlist.c b/desktop/hotlist.c new file mode 100644 index 000000000..09be05709 --- /dev/null +++ b/desktop/hotlist.c @@ -0,0 +1,457 @@ +/* + * Copyright 2004, 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <ctype.h> +#include <stdlib.h> + +#include "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/hotlist.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "desktop/tree_url_node.h" + +#include "utils/messages.h" +#include "utils/utils.h" +#include "utils/log.h" + +#define URL_CHUNK_LENGTH 512 + +static struct tree *hotlist_tree; +static struct node *hotlist_tree_root; + +static bool creating_node; +static hlcache_handle *folder_icon; + +static const struct { + const char *url; + const char *msg_key; +} hotlist_default_entries[] = { + { "http://www.netsurf-browser.org/", "HotlistHomepage" }, + { "http://www.netsurf-browser.org/downloads/riscos/testbuilds", + "HotlistTestBuild" }, + { "http://www.netsurf-browser.org/documentation", + "HotlistDocumentation" }, + { "http://sourceforge.net/tracker/?atid=464312&group_id=51719", + "HotlistBugTracker" }, + { "http://sourceforge.net/tracker/?atid=464315&group_id=51719", + "HotlistFeatureRequest" } +}; +#define HOTLIST_ENTRIES_COUNT (sizeof(hotlist_default_entries) / sizeof(hotlist_default_entries[0])) + +static node_callback_resp hotlist_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + struct node *node = msg_data->node; + const char *text; + char *norm_text; + bool is_folder = tree_node_is_folder(node); + + switch (msg_data->msg) { + case NODE_ELEMENT_EDIT_FINISHED: + if (creating_node && + (is_folder == false) && + (msg_data->flag == TREE_ELEMENT_TITLE)) { + tree_url_node_edit_url(hotlist_tree, node); + } else { + creating_node = false; + } + return NODE_CALLBACK_HANDLED; + + case NODE_ELEMENT_EDIT_FINISHING: + if (creating_node && (is_folder == false)) + return tree_url_node_callback(hotlist_tree, msg_data); + + if (is_folder == true) { + text = msg_data->data.text; + while (isspace(*text)) + text++; + norm_text = strdup(text); + if (norm_text == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NODE_CALLBACK_REJECT; + } + /* don't allow zero length entry text, return false */ + if (norm_text[0] == '\0') { + warn_user("NoNameError", 0); + msg_data->data.text = NULL; + return NODE_CALLBACK_CONTINUE; + } + msg_data->data.text = norm_text; + } + break; + + case NODE_DELETE_ELEMENT_IMG: + return NODE_CALLBACK_HANDLED; + + default: + if (is_folder == false) + return tree_url_node_callback(hotlist_tree, msg_data); + } + + return NODE_CALLBACK_NOT_HANDLED; +} + + +bool hotlist_initialise(struct tree *tree, const char *hotlist_path) +{ + struct node *node; + const struct url_data *url_data; + char *name; + int hlst_loop; + + /* Either load or create a hotlist */ + + creating_node = false; + + folder_icon = tree_load_icon(tree_directory_icon_name); + + tree_url_node_init(); + + if (tree == NULL) + return false; + + hotlist_tree = tree; + hotlist_tree_root = tree_get_root(hotlist_tree); + + if (tree_urlfile_load(hotlist_path, + hotlist_tree, + hotlist_node_callback, + NULL)) { + return true; + } + + + /* failed to load hotlist file, use default list */ + name = strdup("NetSurf"); + if (name == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return false; + } + node = tree_create_folder_node(hotlist_tree, hotlist_tree_root, + name, true, false, false); + if (node == NULL) { + free(name); + return false; + } + + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_set_node_icon(hotlist_tree, node, folder_icon); + + for (hlst_loop = 0; hlst_loop != HOTLIST_ENTRIES_COUNT; hlst_loop++) { + url_data = urldb_get_url_data(hotlist_default_entries[hlst_loop].url); + if (url_data == NULL) { + urldb_add_url(hotlist_default_entries[hlst_loop].url); + urldb_set_url_persistence( + hotlist_default_entries[hlst_loop].url, + true); + url_data = urldb_get_url_data( + hotlist_default_entries[hlst_loop].url); + } + if (url_data != NULL) { + tree_create_URL_node(hotlist_tree, node, + hotlist_default_entries[hlst_loop].url, + messages_get(hotlist_default_entries[hlst_loop].msg_key), + hotlist_node_callback, NULL); + tree_update_URL_node(hotlist_tree, node, + hotlist_default_entries[hlst_loop].url, + url_data, false); + } + } + + + + return true; +} + + +/** + * Get flags with which the hotlist tree should be created; + * + * \return the flags + */ +unsigned int hotlist_get_tree_flags(void) +{ + return TREE_MOVABLE; +} + + +/** + * Deletes the global history tree and saves the hotlist. + * \param hotlist_path the path where the hotlist should be saved + */ +void hotlist_cleanup(const char *hotlist_path) +{ + hotlist_export(hotlist_path); +} + + +/** + * Informs the hotlist that some content has been visited. Internal procedure. + * + * \param content the content visited + * \param node the node to update siblings and children of + */ +static void hotlist_visited_internal(hlcache_handle *content, struct node *node) +{ + struct node *child; + const char *text; + const char *url; + + if (content == NULL || + content_get_url(content) == NULL || + hotlist_tree == NULL) + return; + + url = content_get_url(content); + + for (; node; node = tree_node_get_next(node)) { + if (!tree_node_is_folder(node)) { + text = tree_url_node_get_url(node); + if (strcmp(text, url) == 0) { + tree_update_URL_node(hotlist_tree, node, + url, NULL, false); + } + } + child = tree_node_get_child(node); + if (child != NULL) { + hotlist_visited_internal(content, child); + } + } +} + +/** + * Informs the hotlist that some content has been visited + * + * \param content the content visited + */ +void hotlist_visited(hlcache_handle *content) +{ + if (hotlist_tree != NULL) { + hotlist_visited_internal(content, tree_get_root(hotlist_tree)); + } +} + +/** + * Save the hotlist in a human-readable form under the given location. + * + * \param path the path where the hotlist will be saved + */ +bool hotlist_export(const char *path) +{ + return tree_urlfile_save(hotlist_tree, path, "NetSurf hotlist"); +} + +/** + * Edit the node which is currently selected. Works only if one node is + * selected. + */ +void hotlist_edit_selected(void) +{ + struct node *node; + struct node_element *element; + + node = tree_get_selected_node(hotlist_tree_root); + + if (node != NULL) { + creating_node = true; + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + tree_start_edit(hotlist_tree, element); + } +} + +/** + * Delete nodes which are currently selected. + */ +void hotlist_delete_selected(void) +{ + tree_delete_selected_nodes(hotlist_tree, hotlist_tree_root); +} + +/** + * Select all nodes in the tree. + */ +void hotlist_select_all(void) +{ + tree_set_node_selected(hotlist_tree, hotlist_tree_root, + true, true); +} + +/** + * Unselect all nodes. + */ +void hotlist_clear_selection(void) +{ + tree_set_node_selected(hotlist_tree, hotlist_tree_root, + true, false); +} + +/** + * Expand grouping folders and history entries. + */ +void hotlist_expand_all(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, true, true); +} + +/** + * Expand grouping folders only. + */ +void hotlist_expand_directories(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, true, false); +} + +/** + * Expand history entries only. + */ +void hotlist_expand_addresses(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + true, false, true); +} + +/** + * Collapse grouping folders and history entries. + */ +void hotlist_collapse_all(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + false, true, true); +} + +/** + * Collapse grouping folders only. + */ +void hotlist_collapse_directories(void) +{ + tree_set_node_expanded(hotlist_tree, hotlist_tree_root, + false, true, false); +} + +/** + * Collapse history entries only. + */ +void hotlist_collapse_addresses(void) +{ + tree_set_node_expanded(hotlist_tree, + hotlist_tree_root, false, false, true); +} + +/** + * Add a folder node. + */ +void hotlist_add_folder(void) +{ + struct node *node; + struct node_element *element; + char *title = strdup("Untitled"); + + if (title == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return; + } + creating_node = true; + node = tree_create_folder_node(hotlist_tree, hotlist_tree_root, title, + true, false, false); + if (node == NULL) { + free(title); + return; + } + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_set_node_icon(hotlist_tree, node, folder_icon); + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + tree_start_edit(hotlist_tree, element); +} + +/** + * Add an entry node. + */ +void hotlist_add_entry(void) +{ + struct node *node; + creating_node = true; + node = tree_create_URL_node(hotlist_tree, hotlist_tree_root, "Address", + "Untitled", hotlist_node_callback, NULL); + + if (node == NULL) + return; + tree_set_node_user_callback(node, hotlist_node_callback, NULL); + tree_url_node_edit_title(hotlist_tree, node); +} + +/** + * Adds the currently viewed page to the hotlist + */ +void hotlist_add_page(const char *url) +{ + const struct url_data *data; + struct node *node; + + if (url == NULL) + return; + data = urldb_get_url_data(url); + if (data == NULL) + return; + + node = tree_create_URL_node(hotlist_tree, hotlist_tree_root, url, NULL, + hotlist_node_callback, NULL); + tree_update_URL_node(hotlist_tree, node, url, data, false); +} + +/** + * Adds the currently viewed page to the hotlist at the given cooridinates + * \param url url of the page + * \param x X cooridinate with respect to tree origin + * \param y Y cooridinate with respect to tree origin + */ +void hotlist_add_page_xy(const char *url, int x, int y) +{ + const struct url_data *data; + struct node *link, *node; + bool before; + + data = urldb_get_url_data(url); + if (data == NULL) { + urldb_add_url(url); + urldb_set_url_persistence(url, true); + data = urldb_get_url_data(url); + } + if (data != NULL) { + link = tree_get_link_details(hotlist_tree, x, y, &before); + node = tree_create_URL_node(NULL, NULL, url, + NULL, hotlist_node_callback, NULL); + tree_link_node(hotlist_tree, link, node, before); + } +} + +/** + * Open the selected entries in separate browser windows. + */ +void hotlist_launch_selected(void) +{ + tree_launch_selected(hotlist_tree); +} diff --git a/desktop/hotlist.h b/desktop/hotlist.h new file mode 100644 index 000000000..84f573a90 --- /dev/null +++ b/desktop/hotlist.h @@ -0,0 +1,54 @@ +/* + * Copyright 2004, 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + + +/** \file + * Hotlist (interface). + */ + +#ifndef _NETSURF_DESKTOP_HOTLIST_H_ +#define _NETSURF_DESKTOP_HOTLIST_H_ + +#include <stdbool.h> + +#include "desktop/tree.h" + +bool hotlist_initialise(struct tree *tree, const char *hotlist_path); +unsigned int hotlist_get_tree_flags(void); +void hotlist_cleanup(const char *hotlist_path); + +bool hotlist_export(const char *path); +void hotlist_edit_selected(void); +void hotlist_delete_selected(void); +void hotlist_select_all(void); +void hotlist_clear_selection(void); +void hotlist_expand_all(void); +void hotlist_expand_directories(void); +void hotlist_expand_addresses(void); +void hotlist_collapse_all(void); +void hotlist_collapse_directories(void); +void hotlist_collapse_addresses(void); +void hotlist_add_folder(void); +void hotlist_add_entry(void); +void hotlist_add_page(const char *url); +void hotlist_add_page_xy(const char *url, int x, int y); +void hotlist_launch_selected(void); + +#endif diff --git a/desktop/options.c b/desktop/options.c index 9ed8b192c..47b42dc3b 100644 --- a/desktop/options.c +++ b/desktop/options.c @@ -31,16 +31,10 @@ #include <stdio.h> #include <string.h> #include <strings.h> -#include <libxml/HTMLparser.h> -#include <libxml/HTMLtree.h> -#include "content/urldb.h" #include "css/css.h" #include "desktop/options.h" #include "desktop/plot_style.h" -#include "desktop/tree.h" #include "utils/log.h" -#include "utils/messages.h" -#include "utils/url.h" #include "utils/utils.h" #if defined(riscos) @@ -145,6 +139,7 @@ unsigned int option_min_reflow_period = 100; /* time in cs */ #else unsigned int option_min_reflow_period = 25; /* time in cs */ #endif +char *option_tree_icons_dir = NULL; bool option_core_select_menu = false; /** top margin of exported page*/ int option_margin_top = DEFAULT_MARGIN_TOP_MM; @@ -247,6 +242,7 @@ struct { { "scale", OPTION_INTEGER, &option_scale }, { "incremental_reflow", OPTION_BOOL, &option_incremental_reflow }, { "min_reflow_period", OPTION_INTEGER, &option_min_reflow_period }, + { "tree_icons_dir", OPTION_STRING, &option_tree_icons_dir }, { "core_select_menu", OPTION_BOOL, &option_core_select_menu }, /* Fetcher options */ { "max_fetchers", OPTION_INTEGER, &option_max_fetchers }, @@ -276,13 +272,6 @@ struct { #define option_table_entries (sizeof option_table / sizeof option_table[0]) -static void options_load_tree_directory(xmlNode *ul, struct node *directory); -static void options_load_tree_entry(xmlNode *li, struct node *directory); -xmlNode *options_find_tree_element(xmlNode *node, const char *name); -bool options_save_tree_directory(struct node *directory, xmlNode *node); -bool options_save_tree_entry(struct node *entry, xmlNode *node); - - /** * Read options from a file. * @@ -429,351 +418,3 @@ void options_dump(void) fprintf(stderr, "\n"); } } - -/** - * 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_tree(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_tree_element((xmlNode *) doc, "html"); - body = options_find_tree_element(html, "body"); - ul = options_find_tree_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) { - free(tree); - xmlFreeDoc(doc); - - return NULL; - } - - options_load_tree_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_tree_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((const char *) n->name, "li") == 0) { - /* entry */ - options_load_tree_entry(n, directory); - - } else if (strcmp((const char *) 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((const char *) 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) { - free(title); - - return; - } - options_load_tree_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_tree_entry(xmlNode *li, struct node *directory) { - char *url = NULL, *url1 = NULL; - char *title = NULL; - struct node *entry; - xmlNode *n; - const struct url_data *data; - url_func_result res; - - for (n = li->children; n; n = n->next) { - /* The li must contain an "a" element */ - if (n->type == XML_ELEMENT_NODE && - strcmp((const char *) n->name, "a") == 0) { - url1 = (char *) xmlGetProp(n, (const xmlChar *) "href"); - title = (char *) xmlNodeGetContent(n); - } - } - - if (!url1 || !title) { - warn_user("HotlistLoadError", "(Missing <a> in <li> or " - "memory exhausted.)"); - return; - } - - /* We're loading external input. - * This may be garbage, so attempt to normalise - */ - res = url_normalize(url1, &url); - if (res != URL_FUNC_OK) { - LOG(("Failed normalising '%s'", url1)); - - if (res == URL_FUNC_NOMEM) - warn_user("NoMemory", NULL); - - xmlFree(url1); - xmlFree(title); - - return; - } - - /* No longer need this */ - xmlFree(url1); - - data = urldb_get_url_data(url); - if (!data) { - /* No entry in database, so add one */ - urldb_add_url(url); - /* now attempt to get url data */ - data = urldb_get_url_data(url); - } - if (!data) { - xmlFree(title); - free(url); - - return; - } - - /* Make this URL persistent */ - urldb_set_url_persistence(url, true); - - if (!data->title) - urldb_set_url_title(url, title); - - entry = tree_create_URL_node(directory, url, data, title); - if (entry == NULL) { - /** \todo why isn't this fatal? */ - warn_user("NoMemory", 0); - } - - xmlFree(title); - free(url); -} - - -/** - * 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_tree_element(xmlNode *node, const char *name) { - xmlNode *n; - if (!node) - return 0; - for (n = node->children; - n && !(n->type == XML_ELEMENT_NODE && - strcmp((const char *) 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_tree(struct tree *tree, const char *filename, const char *page_title) { - int res; - xmlDoc *doc; - xmlNode *html, *head, *title, *body; - - /* Unfortunately the Browse Hotlist format is invalid HTML, - * so this is a lie. */ - doc = htmlNewDoc( - (const xmlChar *) "http://www.w3.org/TR/html4/strict.dtd", - (const xmlChar *) "-//W3C//DTD HTML 4.01//EN"); - if (!doc) { - warn_user("NoMemory", 0); - return false; - } - - html = xmlNewNode(NULL, (const xmlChar *) "html"); - if (!html) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return false; - } - xmlDocSetRootElement(doc, html); - - head = xmlNewChild(html, NULL, (const xmlChar *) "head", NULL); - if (!head) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return false; - } - - title = xmlNewTextChild(head, NULL, (const xmlChar *) "title", - (const xmlChar *) page_title); - if (!title) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return false; - } - - body = xmlNewChild(html, NULL, (const xmlChar *) "body", NULL); - if (!body) { - warn_user("NoMemory", 0); - xmlFreeDoc(doc); - return false; - } - - if (!options_save_tree_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_tree_directory(struct node *directory, xmlNode *node) { - struct node *child; - xmlNode *ul, *h4; - - ul = xmlNewChild(node, NULL, (const xmlChar *) "ul", NULL); - if (!ul) - return false; - - for (child = directory->child; child; child = child->next) { - if (!child->folder) { - /* entry */ - if (!options_save_tree_entry(child, ul)) - return false; - } else { - /* directory */ - /* invalid HTML */ - h4 = xmlNewTextChild(ul, NULL, - (const xmlChar *) "h4", - (const xmlChar *) child->data.text); - if (!h4) - return false; - - if (!options_save_tree_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_tree_entry(struct node *entry, xmlNode *node) { - xmlNode *li, *a; - xmlAttr *href; - struct node_element *element; - - li = xmlNewChild(node, NULL, (const xmlChar *) "li", NULL); - if (!li) - return false; - - a = xmlNewTextChild(li, NULL, (const xmlChar *) "a", - (const xmlChar *) entry->data.text); - if (!a) - return false; - - element = tree_find_element(entry, TREE_ELEMENT_URL); - if (!element) - return false; - href = xmlNewProp(a, (const xmlChar *) "href", - (const xmlChar *) element->text); - if (!href) - return false; - return true; -} diff --git a/desktop/options.h b/desktop/options.h index ca92ee90a..2779692b6 100644 --- a/desktop/options.h +++ b/desktop/options.h @@ -85,6 +85,7 @@ extern int option_toolbar_status_width; extern int option_scale; extern bool option_incremental_reflow; extern unsigned int option_min_reflow_period; +extern char *option_tree_icons_dir; extern bool option_core_select_menu; extern int option_margin_top; @@ -114,8 +115,4 @@ void options_read(const char *path); void options_write(const char *path); void options_dump(void); -struct tree *options_load_tree(const char *filename); -bool options_save_tree(struct tree *tree, const char *filename, - const char *page_title); - #endif diff --git a/desktop/sslcert.c b/desktop/sslcert.c new file mode 100644 index 000000000..2d10b4719 --- /dev/null +++ b/desktop/sslcert.c @@ -0,0 +1,276 @@ +/* + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * SSL Certificate verification UI (implementation) + */ + +#include "utils/config.h" + +#include <assert.h> +#include <stdbool.h> +#include <string.h> +#include "content/content.h" +#include "content/fetch.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/sslcert.h" +#include "desktop/tree.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/utils.h" + +/** Flags for each type of ssl tree node. */ +enum tree_element_ssl { + TREE_ELEMENT_SSL_VERSION = 0x01, + TREE_ELEMENT_SSL_VALID_FROM = 0x02, + TREE_ELEMENT_SSL_VALID_TO = 0x03, + TREE_ELEMENT_SSL_CERT_TYPE = 0x04, + TREE_ELEMENT_SSL_SERIAL = 0x05, + TREE_ELEMENT_SSL_ISSUER = 0x06, +}; + +/** ssl certificate verification context. */ +struct sslcert_session_data { + unsigned long num; /**< The number of ssl certificates in the chain */ + char *url; /**< The url of the certificate */ + struct tree *tree; /**< The root of the treeview */ + llcache_query_response cb; /**< callback when cert is accepted or rejected */ + void *cbpw; /**< context passed to callback */ +}; + +/** Handle for the window icon. */ +static hlcache_handle *sslcert_icon; + +/** Initialise ssl certificate window. */ +void sslcert_init(void) +{ + sslcert_icon = tree_load_icon(tree_content_icon_name); +} + + +/** + * Get flags with which the sslcert tree should be created; + * + * \return the flags + */ +unsigned int sslcert_get_tree_flags(void) +{ + return TREE_NO_DRAGS | TREE_NO_SELECT; +} + + +void sslcert_cleanup(void) +{ + return; +} + +struct sslcert_session_data * +sslcert_create_session_data(unsigned long num, + const char *url, + llcache_query_response cb, + void *cbpw) +{ + struct sslcert_session_data *data; + + data = malloc(sizeof(struct sslcert_session_data)); + if (data == NULL) { + warn_user("NoMemory", 0); + return NULL; + } + data->url = strdup(url); + if (data->url == NULL) { + free(data); + warn_user("NoMemory", 0); + return NULL; + } + data->num = num; + data->cb = cb; + data->cbpw = cbpw; + + return data; +} + +static node_callback_resp sslcert_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + if (msg_data->msg == NODE_DELETE_ELEMENT_IMG) + return NODE_CALLBACK_HANDLED; + return NODE_CALLBACK_NOT_HANDLED; +} + +static struct node *sslcert_create_node(const struct ssl_cert_info *cert) +{ + struct node *node; + struct node_element *element; + char *text; + + text = messages_get_buff("SSL_Certificate_Subject", cert->subject); + if (text == NULL) + return NULL; + + node = tree_create_leaf_node(NULL, NULL, text, false, false, false); + if (node == NULL) { + free(text); + return NULL; + } + tree_set_node_user_callback(node, sslcert_node_callback, NULL); + + /* add issuer node */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_ISSUER, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_Issuer", cert->issuer); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + /* add version node */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_VERSION, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_Version", cert->version); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + /* add valid from node */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_VALID_FROM, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_ValidFrom", cert->not_before); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + + /* add valid to node */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_VALID_TO, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_ValidTo", cert->not_after); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + /* add certificate type */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_CERT_TYPE, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_Type", cert->cert_type); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + /* add serial node */ + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_SSL_SERIAL, false); + if (element != NULL) { + text = messages_get_buff("SSL_Certificate_Serial", cert->serial); + if (text == NULL) { + tree_delete_node(NULL, node, false); + return NULL; + } + tree_update_node_element(NULL, element, text, NULL); + } + + /* set the display icon */ + tree_set_node_icon(NULL, node, sslcert_icon); + + return node; +} + +bool sslcert_load_tree(struct tree *tree, + const struct ssl_cert_info *certs, + struct sslcert_session_data *data) +{ + struct node *tree_root; + struct node *node; + unsigned long cert_loop; + + assert(data != NULL && certs != NULL && tree != NULL); + + tree_root = tree_get_root(tree); + + for (cert_loop = 0; cert_loop < data->num; cert_loop++) { + node = sslcert_create_node(&(certs[cert_loop])); + if (node != NULL) { + /* There is no problem creating the node + * add an entry for it in the root of the + * treeview . + */ + tree_link_node(tree, tree_root, node, false); + } + } + + data->tree = tree; + + return tree; + +} + + +static void sslcert_cleanup_session(struct sslcert_session_data *session) +{ + assert(session != NULL); + + free(session->url); + free(session); +} + + + +bool sslcert_reject(struct sslcert_session_data *session) +{ + session->cb(false, session->cbpw); + sslcert_cleanup_session(session); + return true; +} + + +/** + * Handle acceptance of certificate + */ +bool sslcert_accept(struct sslcert_session_data *session) +{ + assert(session != NULL); + + urldb_set_cert_permissions(session->url, true); + + session->cb(true, session->cbpw); + + sslcert_cleanup_session(session); + + return true; +} diff --git a/desktop/sslcert.h b/desktop/sslcert.h new file mode 100644 index 000000000..bc1b8bef8 --- /dev/null +++ b/desktop/sslcert.h @@ -0,0 +1,43 @@ +/* + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _NETSURF_DESKTOP_SSLCERT_H_ +#define _NETSURF_DESKTOP_SSLCERT_H_ + +#include <stdbool.h> + +#include "desktop/tree.h" + +struct sslcert_session_data; + +void sslcert_init(void); +unsigned int sslcert_get_tree_flags(void); +void sslcert_cleanup(void); + +struct sslcert_session_data *sslcert_create_session_data(unsigned long num, + const char *url, llcache_query_response cb, void *cbpw); +bool sslcert_load_tree(struct tree *tree, + const struct ssl_cert_info *certs, + struct sslcert_session_data *data); + +bool sslcert_reject(struct sslcert_session_data *session); +bool sslcert_accept(struct sslcert_session_data *session); + + +#endif diff --git a/desktop/tree.c b/desktop/tree.c index 295bef195..36ba38d7e 100644 --- a/desktop/tree.c +++ b/desktop/tree.c @@ -1,5 +1,6 @@ /* * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -25,135 +26,367 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include "content/urldb.h" +#include "content/content.h" +#include "content/hlcache.h" +#include "desktop/browser.h" +#include "desktop/textarea.h" +#include "desktop/textinput.h" #include "desktop/tree.h" #include "desktop/options.h" +#include "desktop/plotters.h" +#include "render/font.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" +#include "utils/url.h" + +typedef enum { + TREE_NO_DRAG = 0, + TREE_SELECT_DRAG, + TREE_MOVE_DRAG +} tree_drag_type; + + +#define MAXIMUM_URL_LENGTH 1024 + +#define TREE_ICON_SIZE 16 +#define NODE_INSTEP 20 +#define TREE_TEXT_HEIGHT 20 +#define FURNITURE_COLOUR 0x888888 + +static plot_font_style_t plot_fstyle = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 10240, + .weight = 400, + .flags = FONTF_NONE, + .background = 0xFFFFFF, + .foreground = 0x000000 +}; + +static plot_font_style_t plot_fstyle_selected = { + .family = PLOT_FONT_FAMILY_SANS_SERIF, + .size = 10240, + .weight = 400, + .flags = FONTF_NONE, + .background = 0x000000, + .foreground = 0xEEEEEE +}; + +struct node; +struct tree; + +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 */ + const char *text; /**< Text for the element */ + void *bitmap; /**< Bitmap for the element */ + struct node_element *next; /**< Next node element */ + unsigned int flag; /**< Client specified flag for data + being represented */ + bool editable; /**< Whether the node text can be + * modified, editable text is deleted + * without noticing the tree user + */ +}; + +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 retain_in_memory; /**< Whether the node remains + in memory after deletion */ + bool deleted; /**< Whether the node is currently + deleted */ + 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 *last_child; /**< Last child */ + struct node *previous; /**< Previous child of the parent */ + struct node *next; /**< Next child of the parent */ + + /** Sorting function for the node (for folder nodes only) */ + int (*sort) (struct node *, struct node *); + /** Gets called for each deleted node_element and on node launch */ + tree_node_user_callback user_callback; + /** User data to be passed to delete_callback */ + void *callback_data; +}; + +struct tree { + /* These coordinates are only added to the coordinates passed to the + plotters. This means they are invisible to the tree, what has to be + taken into account i.e in keyboard/mouse event passing */ + struct node *root; /* Tree root element */ + int width; /* Tree width */ + int height; /* Tree height */ + unsigned int flags; /* Tree flags */ + struct text_area *textarea; /* Handle for UTF-8 textarea */ + bool textarea_drag_start; /* whether the start of a mouse drag + was in the textarea */ + struct node_element *editing; /* Node element being edited */ + + bool redraw; /* Flag indicating whether the tree + should be redrawn on layout + changes */ + tree_drag_type drag; + const struct treeview_table *callbacks; + void *client_data; /* User assigned data for the + callbacks */ +}; -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, - node_element_data data); -static void tree_delete_node_internal(struct tree *tree, struct node *node, bool siblings); -static int tree_get_node_width(struct node *node); -static int tree_get_node_height(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); -void tree_clear_processing(struct node *node); -struct node *tree_move_processing_node(struct node *node, struct node *link, - bool before, bool first); -struct node *tree_create_leaf_node_shared(struct node *parent, const char *title); -static int tree_initialising = 0; +/** + * Creates and initialises a new tree. + * + * \param flags flag word for flags to create the new tree with + * \param redraw_request function to be called each time the tree wants to + * be redrawn + * \param client_data data to be passed to start_redraw and end_redraw + * \param root gets updated to point at the root of the tree, + * if not NULL + * \return the newly created tree, or NULL on memory exhaustion + */ +struct tree *tree_create(unsigned int flags, + const struct treeview_table *callbacks, void *client_data) +{ + struct tree *tree; + char *title; + + tree = calloc(sizeof(struct tree), 1); + if (tree == NULL) { + LOG(("calloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + + title = strdup("Root"); + if (title == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + free(tree); + return NULL; + } + tree->root = tree_create_folder_node(NULL, NULL, title, + false, false, false); + if (tree->root == NULL) { + free(title); + free(tree); + return NULL; + } + tree->root->expanded = true; + + tree->width = 0; + tree->height = 0; + tree->flags = flags; + tree->textarea = NULL; + tree->textarea_drag_start = false; + tree->editing = NULL; + tree->redraw = false; + tree->drag = TREE_NO_DRAG; + tree->callbacks = callbacks; + tree->client_data = client_data; + + return tree; +} /** - * Initialises a user-created tree + * Recalculates the dimensions of a node element. * - * \param tree the tree to initialise + * \param tree the tree to which the element belongs, may be NULL + * \param element the element to recalculate */ -void tree_initialise(struct tree *tree) { +static void tree_recalculate_node_element(struct tree *tree, + struct node_element *element) +{ + struct bitmap *bitmap = NULL; + int width, height; - assert(tree); + assert(element != NULL); - tree_set_node_expanded(tree, tree->root, true); - tree_initialise_nodes(tree, tree->root); - tree_recalculate_node_positions(tree, tree->root); - tree_set_node_expanded(tree, tree->root, false); - tree->root->expanded = true; - tree_recalculate_node_positions(tree, tree->root); - tree_recalculate_size(tree); + switch (element->type) { + case NODE_ELEMENT_TEXT_PLUS_ICON: + case NODE_ELEMENT_TEXT: + if(element->text == NULL) + break; + + if (tree != NULL && element == tree->editing) { + textarea_get_dimensions(tree->textarea, + &element->box.width, NULL); + } else { + nsfont.font_width(&plot_fstyle, + element->text, + strlen(element->text), + &element->box.width); + } + + element->box.width += 8; + element->box.height = TREE_TEXT_HEIGHT; + + if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) + element->box.width += NODE_INSTEP; + + break; + + case NODE_ELEMENT_BITMAP: + bitmap = element->bitmap; + if (bitmap != NULL) { + width = bitmap_get_width(bitmap); + height = bitmap_get_height(bitmap); + element->box.width = width + 1; + element->box.height = height + 2; + } else { + element->box.width = 0; + element->box.height = 0; + } + break; + } } /** - * Initialises a user-created node structure + * Calculates the height of a node including any children * - * \param root the root node to update from + * \param node the node to calculate the height of + * \return the total height of the node and children */ -void tree_initialise_nodes(struct tree *tree, struct node *root) { - struct node *node; +static int tree_get_node_height(struct node *node) +{ + int y1; - assert(root); + assert(node != NULL); - tree_initialising++; - for (node = root; node; node = node->next) { - tree_recalculate_node(tree, node, true); - if (node->child) { - tree_initialise_nodes(tree, node->child); - } + if ((node->child == NULL) || (node->expanded == false)) { + return node->box.height; + } + + y1 = node->box.y; + if (y1 < 0) { + y1 = 0; } - tree_initialising--; + node = node->child; - if (tree_initialising == 0) - tree_recalculate_node_positions(tree, root); + while ((node->next != NULL) || + ((node->child != NULL) && (node->expanded))) { + for (; node->next != NULL; node = node->next); + + if ((node->child != NULL) && (node->expanded)) { + node = node->child; + } + } + return node->box.y + node->box.height - y1; } /** - * Recalculate the node data and redraw the relevant section of the tree. + * Calculates the width of a node including any children * - * \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 + * \param node the node to calculate the height of + * \return the total width of the node and children */ -void tree_handle_node_changed(struct tree *tree, struct node *node, - bool recalculate_sizes, bool expansion) { - int width, height; +static int tree_get_node_width(struct node *node) +{ + int width = 0; + int child_width; - assert(node); + assert(node != NULL); - if ((expansion) && (node->expanded) && (node->child)) { - tree_set_node_expanded(tree, node->child, false); - tree_set_node_selected(tree, node->child, false); - } + for (; node != NULL; node = node->next) { + if (width < (node->box.x + node->box.width)) { + width = node->box.x + node->box.width; + } - width = node->box.width; - height = node->box.height; - if ((recalculate_sizes) || (expansion)) - tree_recalculate_node(tree, node, true); - if ((node->box.height != height) || (expansion)) { - tree_recalculate_node_positions(tree, 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 ((node->child != NULL) && (node->expanded)) { + child_width = tree_get_node_width(node->child); + if (width < child_width) { + width = child_width; + } + } } - if ((recalculate_sizes) || (expansion)) - tree_recalculate_size(tree); + return width; } /** - * Recalculate the node element and redraw the relevant section of the tree. - * The tree size is not updated. + * Recalculates the position of a node, its siblings and children. * - * \param tree the tree to redraw - * \param element the node element to update + * \param tree the tree to which 'root' belongs + * \param root the root node to update from */ -void tree_handle_node_element_changed(struct tree *tree, struct node_element *element) { - int width, height; +static void tree_recalculate_node_positions(struct tree *tree, + struct node *root) +{ + struct node *parent; + struct node *node; + struct node *child; + struct node_element *element; + int y; + bool has_icon; - assert(element); + for (node = root; node != NULL; node = node->next) { - width = element->box.width; - height = element->box.height; - tree_recalculate_node_element(element); + parent = node->parent; + + if (node->previous != NULL) { + node->box.x = node->previous->box.x; + node->box.y = node->previous->box.y + + tree_get_node_height(node->previous); + } else if (parent != NULL) { + 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 = tree->flags & TREE_NO_FURNITURE + ? -NODE_INSTEP + 4 : 0; + node->box.y = -20; + } + + if (!node->expanded) { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + continue; + } + + if (node->folder) { + node->data.box.x = node->box.x; + node->data.box.y = node->box.y; + tree_recalculate_node_positions(tree, node->child); + } else { + y = node->box.y; + has_icon = false; + for (element = &node->data; element != NULL; + element = element->next) + if (element->type == + NODE_ELEMENT_TEXT_PLUS_ICON) { + has_icon = true; + break; + } + + for (element = &node->data; element != NULL; + element = element->next) { + element->box.x = node->box.x; + if (element->type != + NODE_ELEMENT_TEXT_PLUS_ICON && + has_icon) + element->box.x += NODE_INSTEP; + element->box.y = y; + y += element->box.height; + } + } - if (element->box.height != height) { - tree_recalculate_node(tree, element->parent, false); - tree_redraw_area(tree, 0, element->box.y, 16384, 16384); - } else { - if (element->box.width != width) - tree_recalculate_node(tree, 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); } } @@ -161,520 +394,1279 @@ void tree_handle_node_element_changed(struct tree *tree, struct node_element *el /** * Recalculates the size of a node. * - * \param node the node to update - * \param recalculate_sizes whether the node elements have changed + * \param tree the tree to which node belongs, may be NULL + * \param node the node to update + * \param recalculate_sizes whether the node elements have changed */ -void tree_recalculate_node(struct tree *tree, struct node *node, bool recalculate_sizes) { +static void tree_recalculate_node_sizes(struct tree *tree, struct node *node, + bool recalculate_sizes) +{ struct node_element *element; - int height; + int width, height; - assert(node); + assert(node != NULL); + 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) { + for (element = &node->data; element != NULL; + 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) ? + tree_recalculate_node_element(tree, 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; + element->box.width + element->box.x - + node->box.x; node->box.height += element->box.height; } } else { if (recalculate_sizes) - for (element = &node->data; element; element = element->next) - tree_recalculate_node_element(element); + for (element = &node->data; element != NULL; + element = element->next) + tree_recalculate_node_element(tree, element); else - tree_recalculate_node_element(&node->data); + tree_recalculate_node_element(tree, &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(tree, node); + if (tree != NULL && height != node->box.height) + tree_recalculate_node_positions(tree, tree->root); +} + + +/** + * Creates a folder node with the specified title, and optionally links it into + * the tree. + * + * \param tree the owner tree of 'parent', may be NULL + * \param parent the parent node, or NULL not to link + * \param title the node title (not copied, used directly) + * \param editable if true, the node title will be editable + * \param retain_in_memory if true, the node will stay in memory after deletion + * \param deleted if true, the node is created with the deleted flag + * \return the newly created node. + */ +struct node *tree_create_folder_node(struct tree *tree, struct node *parent, + const char *title, bool editable, bool retain_in_memory, + bool deleted) +{ + struct node *node; + + assert(title != NULL); + + node = calloc(sizeof(struct node), 1); + if (node == NULL) { + LOG(("calloc failed")); + warn_user("NoMemory", 0); + return NULL; } + node->folder = true; + node->retain_in_memory = retain_in_memory; + node->deleted = deleted; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = title; + node->data.flag = TREE_ELEMENT_TITLE; + node->data.editable = editable; + node->sort = NULL; + node->user_callback = NULL; + + tree_recalculate_node_sizes(tree, node, true); + if (parent != NULL) + tree_link_node(tree, parent, node, false); + + return node; } /** - * Recalculates the position of a node, its siblings and children. + * Creates a leaf node with the specified title, and optionally links it into + * the tree. * - * \param root the root node to update from + * \param tree the owner tree of 'parent', may be NULL + * \param parent the parent node, or NULL not to link + * \param title the node title (not copied, used directly) + * \param editable if true, the node title will be editable + * \param retain_in_memory if true, the node will stay in memory after deletion + * \param deleted if true, the node is created with the deleted flag + * \return the newly created node. */ -void tree_recalculate_node_positions(struct tree *tree, struct node *root) { - struct node *parent; +struct node *tree_create_leaf_node(struct tree *tree, struct node *parent, + const char *title, bool editable, bool retain_in_memory, + bool deleted) +{ struct node *node; - struct node *child; + + assert(title != NULL); + + node = calloc(sizeof(struct node), 1); + if (node == NULL) { + LOG(("calloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + + node->folder = false; + node->retain_in_memory = retain_in_memory; + node->deleted = deleted; + node->data.parent = node; + node->data.type = NODE_ELEMENT_TEXT; + node->data.text = title; + node->data.flag = TREE_ELEMENT_TITLE; + node->data.editable = editable; + node->sort = NULL; + node->user_callback = NULL; + + tree_recalculate_node_sizes(tree, node, true); + if (parent != NULL) + tree_link_node(tree, parent, node, false); + + return node; +} + + +/** + * Creates an empty text node element and links it to a node. + * + * \param parent the parent node + * \param type the required element type + * \param flag user assigned flag used for searches + * \return the newly created element. + */ +struct node_element *tree_create_node_element(struct node *parent, + node_element_type type, unsigned int flag, bool editable) +{ 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; + element = calloc(sizeof(struct node_element), 1); + if (element == NULL) + return NULL; + + element->parent = parent; + element->flag = flag; + element->type = type; + element->editable = editable; + element->next = parent->data.next; + parent->data.next = element; + + return element; +} + + +/** + * Inserts a node into the correct place according to the parent's sort function + * + * \param parent the node whose child node 'node' becomes + * \param node the node to be inserted + */ +static void tree_sort_insert(struct node *parent, struct node *node) +{ + struct node *after; + + assert(node != NULL); + assert(parent != NULL); + assert(parent->sort != NULL); + + after = parent->last_child; + while ((after != NULL) && + (parent->sort(node, after) == -1)) + after = after->previous; + + if (after != NULL) { + if (after->next != NULL) + after->next->previous = node; + node->next = after->next; + node->previous = after; + after->next = node; + } else { + node->previous = NULL; + node->next = parent->child; + if (parent->child != NULL) { + parent->child->previous = node; + } + parent->child = node; + } + + if (node->next == NULL) + parent->last_child = node; + + node->parent = parent; +} + + +/** + * Recalculates the size of a tree. + * + * \param tree the tree to recalculate + */ +static void tree_recalculate_size(struct tree *tree) +{ + int width, height; + + assert(tree != NULL); + + 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->callbacks->resized(tree, tree->width, tree->height, + tree->client_data); +} + +/** + * Recalculate the node data and redraw the relevant section of the tree. + * + * \param tree the tree to redraw, may be NULL + * \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 + */ +static void tree_handle_node_changed(struct tree *tree, struct node *node, + bool recalculate_sizes, bool expansion) +{ + int width, height, tree_height; + + assert(node != NULL); + + width = node->box.width; + height = node->box.height; + tree_height = tree->height; + + if ((recalculate_sizes) || (expansion)) { + tree_recalculate_node_sizes(tree, node, true); + } + + if (tree != NULL) { + if ((node->box.height != height) || (expansion)) { + tree_recalculate_node_positions(tree, tree->root); + tree_recalculate_size(tree); + tree_height = (tree_height > tree->height) ? + tree_height : tree->height; + if (tree->redraw) { + tree->callbacks->redraw_request(0, node->box.y, + tree->width, + tree_height - node->box.y, + tree->client_data); + } } else { - node->box.x = tree->no_furniture ? -NODE_INSTEP + 4 : 0; - node->box.y = -40; + width = (width > node->box.width) ? + width : node->box.width; + if (tree->redraw) + tree->callbacks->redraw_request(node->box.x, + node->box.y, + width, node->box.height, + tree->client_data); + if (recalculate_sizes) { + tree_recalculate_size(tree); + } } - if (node->expanded) { - if (node->folder) { - node->data.box.x = node->box.x; - node->data.box.y = node->box.y; - tree_recalculate_node_positions(tree, node->child); + } +} + + +/** + * Links a node to another node. + * + * \param tree the tree in which the link takes place, may be NULL + * \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 tree *tree, struct node *link, struct node *node, + bool before) +{ + + struct node *parent; + bool sort = false; + + assert(link != NULL); + assert(node != NULL); + + if ((link->folder == 0) || (before)) { + parent = node->parent = link->parent; + if (parent->sort) { + sort = true; + } else { + if (before) { + node->next = link; + node->previous = link->previous; + if (link->previous != NULL) + link->previous->next = node; + link->previous = node; + if ((parent != NULL) && (parent->child == link)) + parent->child = node; } 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; - } + node->previous = link; + node->next = link->next; + if (link->next != NULL) + link->next->previous = node; + link->next = node; + if ((parent != NULL) && + (parent->last_child == link)) + parent->last_child = node; } + } + } else { + parent = node->parent = link; + if (parent->sort != NULL) { + sort = true; } else { - node->data.box.x = node->box.x; - node->data.box.y = node->box.y; + node->next = NULL; + if (link->child == NULL) { + link->child = link->last_child = node; + node->previous = NULL; + } else { + link->last_child->next = node; + node->previous = link->last_child; + link->last_child = node; + } } + } + + if (sort) { + tree_sort_insert(parent, node); + } + + tree_handle_node_changed(tree, link, false, true); + + node->deleted = false; } /** - * Calculates the width of a node including any children + * Recalculate the node element and redraw the relevant section of the tree. + * The tree size is not updated. * - * \param node the node to calculate the height of - * \return the total width of the node and children + * \param tree the tree to redraw, may be NULL + * \param element the node element to update */ -int tree_get_node_width(struct node *node) { - int width = 0; - int child_width; +static void tree_handle_node_element_changed(struct tree *tree, + struct node_element *element) +{ + int width, height; - assert(node); + assert(element != NULL); - 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; + width = element->box.width; + height = element->box.height; + tree_recalculate_node_element(tree, element); + + if (element->box.height != height) { + tree_recalculate_node_sizes(tree, element->parent, false); + if ((tree != NULL) && (tree->redraw)) { + tree->callbacks->redraw_request(0, element->box.y, + tree->width + element->box.width - + width, + tree->height - element->box.y + + element->box.height - height, + tree->client_data); + } + } else { + if (element->box.width != width) { + tree_recalculate_node_sizes(tree, element->parent, + false); + } + + if (tree != NULL) { + width = (width > element->box.width) ? width : + element->box.width; + if (tree->redraw) { + tree->callbacks->redraw_request(element->box.x, + element->box.y, + width, + element->box.height, + tree->client_data); + } } } - return width; } /** - * Calculates the height of a node including any children + * Stops editing a node_element * - * \param node the node to calculate the height of - * \return the total height of the node and children + * \param tree The tree to stop editing for + * \param keep_changes If true the changes made to the text will be kept, + * if false they will be dropped */ -int tree_get_node_height(struct node *node) { - int y1; +static void tree_stop_edit(struct tree *tree, bool keep_changes) +{ + int text_len; + char *text = NULL; + struct node_element *element; + struct node_msg_data msg_data; + node_callback_resp response; - assert(node); + assert(tree != NULL); - 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; + if (tree->editing == NULL || tree->textarea == NULL) + return; + + element = tree->editing; + + if (keep_changes) { + text_len = textarea_get_text(tree->textarea, NULL, 0); + text = malloc(text_len * sizeof(char)); + if (text == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + textarea_destroy(tree->textarea); + tree->textarea = NULL; + return; } - return node->box.y + node->box.height - y1; - } else { - return node->box.height; + textarea_get_text(tree->textarea, text, text_len); + } + + + if (keep_changes && element->parent->user_callback != NULL) { + msg_data.msg = NODE_ELEMENT_EDIT_FINISHING; + msg_data.flag = element->flag; + msg_data.node = element->parent; + msg_data.data.text = text; + response = element->parent->user_callback( + element->parent->callback_data, + &msg_data); + + switch (response) { + case NODE_CALLBACK_REJECT: + free(text); + text = NULL; + break; + case NODE_CALLBACK_CONTINUE: + free(text); + text = NULL; + return; + case NODE_CALLBACK_HANDLED: + case NODE_CALLBACK_NOT_HANDLED: + text = msg_data.data.text; + break; + } + } + + textarea_destroy(tree->textarea); + tree->textarea = NULL; + tree->editing = NULL; + + if (text != NULL) + tree_update_node_element(tree, element, text, NULL); + else + tree_handle_node_element_changed(tree, element); + + + tree_recalculate_size(tree); + if (element->parent->user_callback != NULL) { + msg_data.msg = NODE_ELEMENT_EDIT_FINISHED; + msg_data.flag = element->flag; + msg_data.node = element->parent; + element->parent->user_callback(element->parent->callback_data, + &msg_data); + } +} + + +/** + * Delinks a node from the tree structures. + * + * \param tree the tree in which the delink takes place, may be NULL + * \param node the node to delink + */ +void tree_delink_node(struct tree *tree, struct node *node) +{ + struct node *parent; + + assert(node != NULL); + + /* do not remove the root */ + if (tree != NULL && node == tree->root) + return; + if ((tree != NULL) && (tree->editing != NULL)) { + parent = tree->editing->parent; + while (parent != NULL) { + if (node == parent) { + tree_stop_edit(tree, false); + break; + } + parent = parent->parent; + } + } + + if (node->parent->child == node) + node->parent->child = node->next; + if (node->parent->last_child == node) + node->parent->last_child = node->previous; + parent = node->parent; + node->parent = NULL; + + if (node->previous != NULL) + node->previous->next = node->next; + if (node->next != NULL) + node->next->previous = node->previous; + node->previous = NULL; + node->next = NULL; + + tree_handle_node_changed(tree, parent, false, true); +} + + +/** + * Deletes a node from the tree. + * + * \param tree the tree to delete from, may be NULL + * \param node the node to delete + * \param siblings whether to delete all siblings + */ +static void tree_delete_node_internal(struct tree *tree, struct node *node, + bool siblings) +{ + struct node *next, *child, *parent; + struct node_element *e, *f; + node_callback_resp response; + struct node_msg_data msg_data; + + assert(node != NULL); + + if (tree != NULL && tree->root == node) + return; + + next = node->next; + parent = node->parent; + if (tree != NULL && parent == tree->root) + parent = NULL; + tree_delink_node(tree, node); + child = node->child; + node->child = NULL; + + node->deleted = true; + if (child != NULL) + tree_delete_node_internal(tree, child, true); + + if (!node->retain_in_memory) { + node->retain_in_memory = true; + for (e = &node->data; e != NULL; e = f) { + if (e->text != NULL) { + response = NODE_CALLBACK_NOT_HANDLED; + if (!e->editable && + node->user_callback != NULL) { + msg_data.msg = NODE_DELETE_ELEMENT_TXT; + msg_data.flag = e->flag; + msg_data.node = node; + msg_data.data.text = (void *)e->text; + response = node->user_callback( + node->callback_data, + &msg_data); + } + if (response != NODE_CALLBACK_HANDLED) + free((void *)e->text); + e->text = NULL; + } + if (e->bitmap != NULL) { + response = NODE_CALLBACK_NOT_HANDLED; + if (node->user_callback != NULL) { + msg_data.msg = NODE_DELETE_ELEMENT_IMG; + msg_data.flag = e->flag; + msg_data.node = node; + msg_data.data.bitmap = + (void *)e->bitmap; + response = node->user_callback( + node->callback_data, + &msg_data); + } + /* TODO the type of this field is platform + dependent */ + if (response != NODE_CALLBACK_HANDLED) + free(e->bitmap); + e->bitmap = NULL; + } + f = e->next; + if (e != &node->data) + free(e); + } + free(node); } + + if (siblings && next) + tree_delete_node_internal(tree, next, true); + if ((tree->flags & TREE_DELETE_EMPTY_DIRS) && parent != NULL && + parent->child == NULL && !parent->deleted) + tree_delete_node_internal(tree, parent, false); +} + + +/** + * Deletes all nodes of a tree and the tree itself. + * + * \param tree the tree to be deleted + */ +void tree_delete(struct tree *tree) +{ + tree_set_redraw(tree, false); + if (tree->root->child != NULL) + tree_delete_node_internal(tree, tree->root->child, true); + + free((void *)tree->root->data.text); + free(tree->root); + free(tree); +} + + +/** + * Gets the redraw property of the given tree. + * + * \param tree the tree for which to retrieve the property + * \return the redraw property of the tree + */ +bool tree_get_redraw(struct tree *tree) +{ + return tree->redraw; } /** - * Updates all siblinds and descendants of a node to an expansion state. + * Deletes a node from the tree. + * + * \param tree the tree to delete from, may be NULL + * \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) +{ + int y = node->box.y; + tree_delete_node_internal(tree, node, siblings); + tree_recalculate_node_positions(tree, tree->root); + if (tree->redraw) + tree->callbacks->redraw_request(0, y, tree->width, tree->height, + tree->client_data); + tree_recalculate_size(tree); +} + + +/** + * Sets an icon for a node + * + * \param tree The tree to which node belongs, may be NULL + * \param node The node for which the icon is set + * \param icon the image to use + */ +void tree_set_node_icon(struct tree *tree, struct node *node, + hlcache_handle *icon) +{ + node->data.type = NODE_ELEMENT_TEXT_PLUS_ICON; + tree_update_node_element(tree, &(node->data), NULL, icon); +} + + +/** + * Updates all siblings 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 + * \param tree the tree to which 'node' belongs + * \param node the node to set all siblings and descendants of + * \param expanded the expansion state to set */ -void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded) { - for (; node; node = node->next) { +static void tree_set_node_expanded_all(struct tree *tree, struct node *node, + bool expanded) +{ + for (; node != NULL; node = node->next) { if (node->expanded != expanded) { node->expanded = expanded; - tree_recalculate_node(tree, node, false); + tree_recalculate_node_sizes(tree, node, false); } - if ((node->child) && (node->expanded)) - tree_set_node_expanded(tree, node->child, expanded); + if ((node->child != NULL) && (node->expanded)) + tree_set_node_expanded_all(tree, node->child, expanded); } } /** - * Updates all siblinds and descendants of a node to an expansion state. + * Updates [all siblings 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 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 + * \param folder whether to update folders, if this together with leaf + * will be false only 'node' will be updated + * \param leaf whether to update leaves (check also description for folder) + * \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; +static bool tree_set_node_expanded_internal(struct tree *tree, + struct node *node, bool expanded, bool folder, bool leaf) +{ bool redraw = false; + struct node *end = (folder == false && leaf == false) ? + node->next : NULL; + + if (tree->editing != NULL && node == tree->editing->parent) + tree_stop_edit(tree, false); - for (; node; node = node->next) { + for (; node != end; node = node->next) { if ((node->expanded != expanded) && (node != tree->root) && - ((folder && (node->folder)) || (leaf && (!node->folder)))) { + ((folder && (node->folder)) || + (leaf && (!node->folder)) || + (!folder && !leaf))) { node->expanded = expanded; - if (node->child) - tree_set_node_expanded(tree, node->child, false); - if ((node->data.next) && (node->data.next->box.height == 0)) - tree_recalculate_node(tree, node, true); + if (node->child != NULL) + tree_set_node_expanded_all(tree, + node->child, false); + if ((node->data.next != NULL) && + (node->data.next->box.height == 0)) + tree_recalculate_node_sizes(tree, node, true); else - tree_recalculate_node(tree, node, false); + tree_recalculate_node_sizes(tree, 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, tree->root); - tree_redraw_area(tree, 0, 0, 16384, 16384); - tree_recalculate_size(tree); + if ((folder || leaf) && (node->child != NULL) && + (node->expanded)) + redraw |= tree_set_node_expanded_internal(tree, + node->child, expanded, folder, leaf); } return redraw; } /** - * Updates all siblinds and descendants of a node to an selected state. - * The required areas of the tree are redrawn. + * Updates [all siblings 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, if this together with leaf + * will be false only 'node' will be updated + * \param leaf whether to update leaves (check also description for folder) + */ +void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded, + bool folder, bool leaf) +{ + if (tree_set_node_expanded_internal(tree, node, expanded, folder, leaf)) + tree_handle_node_changed(tree, node, false, true); +} + + +/** + * Updates a node to an selected state. The required areas of the tree are + * redrawn. * - * \param tree the tree to update nodes for + * \param tree the tree to update nodes for, may be NULL * \param node the node to set all siblings and descendants of + * \param all if true update node together with its siblings and + * descendants * \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)) { +void tree_set_node_selected(struct tree *tree, struct node *node, bool all, + bool selected) +{ + struct node *end; + + if (tree != NULL && node == tree->root) + node = tree->root->child; + if (node == NULL) + return; + + end = all ? NULL : node->next; + + for (; node != end; node = node->next) { + if (node->selected != selected) { node->selected = selected; - tree_redraw_area(tree, node->box.x, node->box.y, node->box.width, - node->data.box.height); + if (tree != NULL && tree->redraw) + tree->callbacks->redraw_request(node->box.x, + node->box.y, + node->box.width, + node->data.box.height, + tree->client_data); } - if ((node->child) && (node->expanded)) - tree_set_node_selected(tree, node->child, selected); + if (all && (node->child != NULL) && (node->expanded)) + tree_set_node_selected(tree, node->child, all, + selected); } } /** - * Finds a node at a specific location. + * Sets the sort function for a node * - * \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 + * \param tree the tree to which 'node' belongs, may be NULL + * \param node the node to be inserted + * \param sort pointer to the sorting function */ -struct node *tree_get_node_at(struct node *root, int x, int y, bool *furniture) { - struct node_element *result; +void tree_set_node_sort_function(struct tree *tree, struct node *node, + int (*sort) (struct node *, struct node *)) +{ + struct node *child; - if ((result = tree_get_node_element_at(root, x, y, furniture))) - return result->parent; - return NULL; + node->sort = sort; + + if (tree != NULL && tree->editing != NULL) + tree_stop_edit(tree, false); + + /* the node had already some children so they must get sorted */ + if (node->child != NULL) { + + child = node->child; + node->child = NULL; + + while (child != NULL) { + tree_sort_insert(node, child); + child = child->next; + } + + } + + if (tree != NULL) + tree_recalculate_node_positions(tree, node->child); } /** - * Finds a node element at a specific location. + * Sets the delete callback for a node. * - * \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 + * \param node the node for which the callback is set + * \param callback the callback functions to be set + * \param data user data to be passed to callback */ -struct node_element *tree_get_node_element_at(struct node *node, int x, int y, - bool *furniture) { - struct node_element *element; +void tree_set_node_user_callback(struct node *node, + tree_node_user_callback callback, void *data) +{ + node->user_callback = callback; + node->callback_data = data; +} - *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; +/** + * Sets the redraw property to the given value. If redraw is true, the tree will + * be redrawn on layout/appearance changes. + * + * \param tree the tree for which the property is set + * \param redraw the value to set + */ +void tree_set_redraw(struct tree *tree, bool redraw) +{ + /* the tree might have no graphical representation, do not set the + redraw flag in such case */ + if (tree->callbacks == NULL) + return; + tree->redraw = redraw; +} + + +/** + * Checks whether a node, its siblings or any children are selected. + * + * \param node the root node to check from + * \return whether 'node', its siblings or any children are selected. + */ +bool tree_node_has_selection(struct node *node) +{ + for (; node != NULL; node = node->next) { + if (node->selected) + return true; + if ((node->child != NULL) && (node->expanded) && + (tree_node_has_selection(node->child))) + return true; } - return NULL; + return false; } /** - * Finds a node element from a node with a specific user_type + * Returns the current value of the nodes deleted property. * - * \param node the node to examine - * \param user_type the user_type to check for - * \return the corresponding element + * \param node the node to be checked + * \return the current value of the nodes deleted property */ -struct node_element *tree_find_element(struct node *node, node_element_data data) { - struct node_element *element; - for (element = &node->data; element; element = element->next) - if (element->data == data) return element; - return NULL; +bool tree_node_is_deleted(struct node *node) +{ + return node->deleted; } /** - * Moves nodes within a tree. + * Returns true if the node is a folder * - * \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 + * \param node the node to be checked + * \return true if the node is a folder, false otherwise */ -void tree_move_selected_nodes(struct tree *tree, struct node *destination, bool before) { - struct node *link; - struct node *test; - bool error; +bool tree_node_is_folder(struct node *node) +{ + return node->folder; +} - tree_clear_processing(tree->root); - tree_selected_to_processing(tree->root); - /* the destination node cannot be a child of any node with the processing flag set */ - error = destination->processing; - for (test = destination; test; test = test->parent) - error |= test->processing; - if (error) { - tree_clear_processing(tree->root); - return; - } - if ((destination->folder) && (!destination->expanded) && (!before)) { - destination->expanded = true; - tree_handle_node_changed(tree, destination, false, true); +/** + * Update the text of a node element if it has changed. + * + * \param element The node element to update. + * \param text The text to update the element with. The ownership of + * this string is taken by this function and must not be + * referred to after the function exits. + */ +bool tree_update_element_text(struct tree *tree, + struct node_element *element, char *text) +{ + const char *node_text; /* existing node text */ + + if (text == NULL) + return false; + + if (element == NULL) { + free(text); + return false; } - link = tree_move_processing_node(tree->root, destination, before, true); - while (link) - link = tree_move_processing_node(tree->root, link, false, false); - tree_clear_processing(tree->root); - tree_recalculate_node_positions(tree, tree->root); - tree_redraw_area(tree, 0, 0, 16384, 16384); + node_text = tree_node_element_get_text(element); + + if ((node_text == NULL) || (strcmp(node_text, text) != 0)) { + tree_update_node_element(tree, element, text, NULL); + } else { + /* text does not need changing, free it */ + free(text); + } + return true; } /** - * Sets the processing flag to the selection state. + * Updates the content of a node_element. * - * \param node the node to process siblings and children of + * \param tree the tree owning element, may be NULL + * \param element the element to be updated + * \param text new text to be set, may be NULL + * \param bitmap new bitmap to be set, may be NULL */ -void tree_selected_to_processing(struct node *node) { - for (; node; node = node->next) { - node->processing = node->selected; - if ((node->child) && (node->expanded)) - tree_selected_to_processing(node->child); +void tree_update_node_element(struct tree *tree, struct node_element *element, + const char *text, void *bitmap) +{ + node_callback_resp response; + struct node_msg_data msg_data; + + assert(element != NULL); + + if (tree != NULL && element == tree->editing) + tree_stop_edit(tree, false); + + if (text != NULL && (element->type == NODE_ELEMENT_TEXT || + element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) { + if (element->text != NULL) { + response = NODE_CALLBACK_NOT_HANDLED; + if (!element->editable && + element->parent->user_callback != + NULL) { + msg_data.msg = NODE_DELETE_ELEMENT_TXT; + msg_data.flag = element->flag; + msg_data.node = element->parent; + msg_data.data.text = (void *)element->text; + response = element->parent->user_callback( + element->parent->callback_data, + &msg_data); + } + if (response != NODE_CALLBACK_HANDLED) + free((void *)element->text); + } + element->text = text; + } + + if (bitmap != NULL && (element->type == NODE_ELEMENT_BITMAP || + element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) { + if (element->bitmap != NULL) { + response = NODE_CALLBACK_NOT_HANDLED; + if (element->parent->user_callback != NULL) { + msg_data.msg = NODE_DELETE_ELEMENT_IMG; + msg_data.flag = element->flag; + msg_data.node = element->parent; + msg_data.data.bitmap = (void *)element->bitmap; + response = element->parent->user_callback( + element->parent->callback_data, + &msg_data); + } + if (response != NODE_CALLBACK_HANDLED) + free(element->bitmap); + } + element->bitmap = bitmap; } + + tree_handle_node_element_changed(tree, element); } /** - * Clears the processing flag. + * Returns the node element's text * - * \param node the node to process siblings and children of + * \return the node element's text */ -void tree_clear_processing(struct node *node) { - for (; node; node = node->next) { - node->processing = false; - if (node->child) - tree_clear_processing(node->child); - } +const char *tree_node_element_get_text(struct node_element *element) +{ + return element->text; } /** - * Moves the first node in a tree with the processing flag set. + * Get the root node of a tree * - * \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 + * \param tree the tree to get the root of + * \return the root of the tree */ -struct node *tree_move_processing_node(struct node *node, struct node *link, bool before, - bool first) { - struct node *result; +struct node *tree_get_root(struct tree *tree) +{ + return tree->root; +} - 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; + +/** + * Returns whether the current tree is being edited at this time + * + * \param tree the tree to be checked + * \return true if the tree is currently being edited + */ +bool tree_is_edited(struct tree *tree) +{ + return tree->editing == NULL ? false : true; } + /** - * Checks whether a node, its siblings or any children are selected. + * Returns the first child of a node * - * \param node the root node to check from + * \param node the node to get the child of + * \return the nodes first child */ -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; +struct node *tree_node_get_child(struct node *node) +{ + return node->child; } /** - * Updates the selected state for a region of nodes. + * Returns the closest sibling a node * - * \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 + * \param node the node to get the sibling of + * \return the nodes sibling */ -void tree_handle_selection_area(struct tree *tree, int x, int y, int width, int height, - bool invert) { - assert(tree); - assert(tree->root); +struct node *tree_node_get_next(struct node *node) +{ + return node->next; +} - if (!tree->root->child) return; - if (width < 0) { - x += width; - width = -width; - } - if (height < 0) { - y += height; - height = -height; +/** + * Draws an elements expansion icon + * + * \param tree the tree to draw the expansion for + * \param element the element to draw the expansion for + * \param tree_x X coordinate of the tree + * \param tree_y Y coordinate of the tree + */ +static void tree_draw_node_expansion(struct tree *tree, struct node *node, + int tree_x, int tree_y) +{ + int x, y; + + assert(tree != NULL); + assert(node != NULL); + + if ((node->child != NULL) || (node->data.next != NULL)) { + x = tree_x + node->box.x - (NODE_INSTEP / 2) - 4; + y = tree_y + node->box.y - (TREE_TEXT_HEIGHT / 2) + 16; + plot.rectangle(x, y, x + 9, y + 9, plot_style_fill_white); + plot.rectangle(x , y, x + 8, y + 8, + plot_style_stroke_darkwbasec); + plot.line(x + 2, y + 4, x + 7, y + 4, + plot_style_stroke_darkwbasec); + if (!node->expanded) + plot.line(x + 4, y + 2, x + 4, y + 7, + plot_style_stroke_darkwbasec); + } - tree_handle_selection_area_node(tree, tree->root->child, x, y, width, height, invert); } /** - * Updates the selected state for a region of nodes. + * Draws an element, including any expansion icons * - * \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 + * \param tree the tree to draw an element for + * \param element the element to draw + * \param tree_x X coordinate of the tree + * \param tree_y Y coordinate of the tree */ -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_draw_node_element(struct tree *tree, + struct node_element *element, int tree_x, int tree_y) +{ + + struct bitmap *bitmap = NULL; + int x, y, width; + bool selected = false; + hlcache_handle *icon; + plot_font_style_t *fstyle; + + assert(tree != NULL); + assert(element != NULL); + assert(element->parent != NULL); + + x = tree_x + element->box.x; + y = tree_y + element->box.y; + width = element->box.width; + if (&element->parent->data == element) + if (element->parent->selected) + selected = true; + + switch (element->type) { + case NODE_ELEMENT_TEXT_PLUS_ICON: + icon = element->bitmap; + if (icon != NULL && + (content_get_status(icon) == + CONTENT_STATUS_READY || + content_get_status(icon) == + CONTENT_STATUS_DONE)) { + content_redraw(icon , x, y + 3, + TREE_ICON_SIZE, TREE_ICON_SIZE, + x, y, x + TREE_ICON_SIZE, + y + TREE_ICON_SIZE, 1, 0); + } + + x += NODE_INSTEP; + width -= NODE_INSTEP; + + /* fall through */ + case NODE_ELEMENT_TEXT: + if (element->text == NULL) + break; + + if (element == tree->editing) + return; + + if (selected) { + fstyle = &plot_fstyle_selected; + plot.rectangle(x, y, x + width, + y + element->box.height, + plot_style_fill_black); + } else { + fstyle = &plot_fstyle; + plot.rectangle(x, y, x + width, + y + element->box.height, + plot_style_fill_white); + } + + plot.text(x + 4, y + TREE_TEXT_HEIGHT * 0.75, + element->text, strlen(element->text), + fstyle); + break; + case NODE_ELEMENT_BITMAP: + bitmap = element->bitmap; + if (bitmap == NULL) + break; + plot.bitmap(x, y, element->box.width - 1, + element->box.height - 2, + bitmap, 0xFFFFFF, BITMAPF_NONE); + if (!(tree->flags & TREE_NO_FURNITURE)) + plot.rectangle(x, y, x + element->box.width - 1, + y + element->box.height - 3, + plot_style_stroke_darkwbasec); + + break; + } + +} + +/** + * Redraws a node. + * + * \param tree the tree to draw + * \param node the node to draw children and siblings of + * \param tree_x X coordinate of the tree + * \param tree_y Y coordinate of the tree + * \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 + */ +static void tree_draw_node(struct tree *tree, struct node *node, + int tree_x, int tree_y, + int clip_x, int clip_y, + int clip_width, int clip_height) +{ struct node_element *element; - struct node *update; + struct node *parent; int x_max, y_max; + int x0, y0, x1, y1; - assert(tree); - assert(node); + assert(tree != NULL); + assert(node != NULL); - x_max = x + width; - y_max = y + height; - for (; node; node = node->next) { + x_max = clip_x + clip_width + NODE_INSTEP; + y_max = clip_y + clip_height; + + if ((node->parent->next != NULL) && + (node->parent->next->box.y < clip_y)) + return; + + for (; node != NULL; node = node->next) { if (node->box.y > y_max) return; + if ((node->next != NULL) && + (!(tree->flags & TREE_NO_FURNITURE))) { + x0 = x1 = tree_x + node->box.x - (NODE_INSTEP / 2); + y0 = tree_y + node->box.y + (20 / 2); + y1 = y0 + node->next->box.y - node->box.y; + plot.line(x0, y0, x1, y1, plot_style_stroke_darkwbasec); + } 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; - } + (node->box.x + node->box.width + + NODE_INSTEP >= clip_x) && + (node->box.y + node->box.height >= clip_y)) { + if (!(tree->flags & TREE_NO_FURNITURE)) { + if ((node->expanded) && (node->child != NULL)) { + x0 = x1 = tree_x + node->box.x + + (NODE_INSTEP / 2); + y0 = tree_y + node->data.box.y + + node->data.box.height; + y1 = y0 + (20 / 2); + plot.line(x0, y0, x1, y1, + plot_style_stroke_darkwbasec); + } - } 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); + parent = node->parent; + if ((parent != NULL) && + (parent != tree->root) && + (parent->child == node)) { + x0 = x1 = tree_x + parent->box.x + + (NODE_INSTEP / 2); + y0 = tree_y + parent->data.box.y + + parent->data.box.height; + y1 = y0 + (20 / 2); + plot.line(x0, y0, x1, y1, + plot_style_stroke_darkwbasec); } + x0 = tree_x + node->box.x - (NODE_INSTEP / 2); + x1 = x0 + (NODE_INSTEP / 2) - 2; + y0 = y1 = tree_y + node->data.box.y + + node->data.box.height - + (20 / 2); + plot.line(x0, y0, x1, y1, + plot_style_stroke_darkwbasec); + tree_draw_node_expansion(tree, node, + tree_x, tree_y); } + if (node->expanded) + for (element = &node->data; element != NULL; + element = element->next) + tree_draw_node_element(tree, element, + tree_x, tree_y); + else + tree_draw_node_element(tree, &node->data, + tree_x, tree_y); } - if ((node->child) && (node->expanded)) - tree_handle_selection_area_node(tree, node->child, x, y, width, height, - invert); + if ((node->child != NULL) && (node->expanded)) + tree_draw_node(tree, node->child, tree_x, tree_y, + clip_x, clip_y, + clip_width, clip_height); } } @@ -682,87 +1674,214 @@ void tree_handle_selection_area_node(struct tree *tree, struct node *node, int x /** * 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 + * \param tree the tree to draw + * \param x X coordinate to draw the tree at + * \param y Y coordinate to draw the tree at + * \param clip_x the minimum x of the clipping rectangle relative to + * the tree origin + * \param clip_y the minimum y of the clipping rectangle relative to + * the tree origin + * \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); +void tree_draw(struct tree *tree, int x, int y, + int clip_x, int clip_y, int clip_width, int clip_height) +{ + int absolute_x, absolute_y; + assert(tree != NULL); + assert(tree->root != NULL); + + /* don't draw empty trees or trees with redraw flag set to false */ + if (tree->root->child == NULL || !tree->redraw) return; + + absolute_x = x + clip_x; + absolute_y = y + clip_y; + plot.rectangle(absolute_x, absolute_y, + absolute_x + clip_width, absolute_y + clip_height, + plot_style_fill_white); + plot.clip(absolute_x, absolute_y, + absolute_x + clip_width, absolute_y + clip_height); + tree_draw_node(tree, tree->root->child, x, y, clip_x, + clip_y, clip_width, clip_height); + if (tree->editing != NULL) { + x = x + tree->editing->box.x; + y = y + tree->editing->box.y; + if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) + x += NODE_INSTEP; + textarea_redraw(tree->textarea, x, y, absolute_x, absolute_y, + absolute_x + clip_width, + absolute_y + clip_height); + } +} + - if (!tree->root->child) return; +/** + * Finds a node element from a node with a specific user_type + * + * \param node the node to examine + * \param flag user assinged flag used is searches + * \param after if this is not NULL the search will start after the given + * node_element + * \return the corresponding element + */ +struct node_element *tree_node_find_element(struct node *node, + unsigned int flag, struct node_element *after) +{ + struct node_element *element; - tree_initialise_redraw(tree); - tree_draw_node(tree, tree->root->child, clip_x, - clip_y, clip_width, clip_height); + if (after == NULL) + element = &node->data; + else { + assert(after->parent == node); + element = after->next; + } + + for (; element != NULL; element = element->next) + if (element->flag == flag) return element; + + return NULL; } /** - * Redraws a node. + * Deletes all selected nodes from the tree. * - * \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 + * \param tree the tree to delete from + * \param node the node to delete */ -void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y, - int clip_width, int clip_height) { +void tree_delete_selected_nodes(struct tree *tree, struct node *node) +{ + struct node *next; - struct node_element *element; - int x_max, y_max; + if (node == tree->root) { + if (node->child != NULL) + tree_delete_selected_nodes(tree, node->child); + return; + } - assert(tree); - assert(node); + while (node != NULL) { + next = node->next; + if (node->selected) + tree_delete_node(tree, node, false); + else if (node->child != NULL) + tree_delete_selected_nodes(tree, node->child); + node = next; + } +} - 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; +/** + * 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->box.y > y_max) return; - if ((node->next) && (!tree->no_furniture)) - tree_draw_line(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 (!tree->no_furniture) { - if ((node->expanded) && (node->child)) - tree_draw_line(node->box.x + (NODE_INSTEP / 2), - node->data.box.y + node->data.box.height, 0, - (40 / 2)); - if ((node->parent) && (node->parent != tree->root) && - (node->parent->child == node)) - tree_draw_line(node->parent->box.x + (NODE_INSTEP / 2), - node->parent->data.box.y + - node->parent->data.box.height, 0, - (40 / 2)); - tree_draw_line(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); + for (; node != NULL; node = node->next) { + if (node->selected) { + if (result != NULL) + return NULL; + result = node; + } + if ((node->child != NULL) && (node->expanded)) { + temp = tree_get_selected_node(node->child); + if (temp != NULL) { + if (result != NULL) + return NULL; + else + result = temp; + } + } + } + return result; +} + + +/** + * 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 + */ +static struct node_element *tree_get_node_element_at(struct node *node, + int x, int y, bool *furniture) +{ + struct node_element *element; + int x0, x1, y0, y1; + + *furniture = false; + for (; node != NULL; 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 != NULL; + element = element->next) { + x0 = element->box.x; + y0 = element->box.y; + x1 = element->box.x + + element->box.width; + y1 = element->box.y + + element->box.height; + if ((x0 < x) && (y0 < y) && (x1 >= x) + && (y1 >= y)) + return element; + } + } else { + x0 = node->data.box.x; + y0 = node->data.box.y; + x1 = node->data.box.x + node->data.box.width; + y1 = node->data.box.y + node->data.box.height; + if ((x0 < x) && (y0 < y) && (x1 >= x) && + (y1>= y)) + return &node->data; + } + if (((node->child != NULL) || + (node->data.next != NULL)) && + (node->data.box.x - NODE_INSTEP + 4 < x) + && (node->data.box.y + 4 < y) && + (node->data.box.x > x) && + (node->data.box.y + 20 > y)) { + *furniture = true; + return &node->data; } - 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); + + element = tree_get_node_element_at(node->child, x, y, + furniture); + if ((node->child != NULL) && (node->expanded) && + (element != NULL)) + return element; } + return NULL; +} + + +/** + * 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 + */ +static 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; } @@ -773,24 +1892,27 @@ void tree_draw_node(struct tree *tree, struct node *node, int clip_x, int clip_y * \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 + * \return the node to link with */ -struct node *tree_get_link_details(struct tree *tree, int x, int y, bool *before) { +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); + assert(tree != NULL); + assert(tree->root != NULL); *before = false; - if (tree->root->child) + if (tree->root->child != NULL) node = tree_get_node_at(tree->root->child, x, y, &furniture); - if ((!node) || (furniture)) + if ((node == NULL) || (furniture)) return tree->root; if (y < (node->box.y + (node->box.height / 2))) { *before = true; - } else if ((node->folder) && (node->expanded) && (node->child)) { + } else if ((node->folder) && (node->expanded) && + (node->child != NULL)) { node = node->child; *before = true; } @@ -799,527 +1921,687 @@ struct node *tree_get_link_details(struct tree *tree, int x, int y, bool *before /** - * Links a node into the tree. + * Launches all the selected nodes of 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 + * \param tree the tree for which all nodes will be launched + * \param node the node which will be checked together with its children */ -void tree_link_node(struct node *link, struct node *node, bool before) { - 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; +static void tree_launch_selected_internal(struct tree *tree, struct node *node) +{ + struct node_msg_data msg_data; + + for (; node != NULL; node = node->next) { + if (node->selected && node->user_callback != NULL) { + msg_data.msg = NODE_LAUNCH; + msg_data.flag = TREE_ELEMENT_TITLE; + msg_data.node = node; + node->user_callback(node->callback_data, &msg_data); } - } else { - if (!link->child) { - link->child = link->last_child = node; - node->previous = NULL; - } else { - link->last_child->next = node; - node->previous = link->last_child; - link->last_child = node; - } - node->parent = link; - node->next = NULL; + if (node->child != NULL) + tree_launch_selected_internal(tree, node->child); } - node->deleted = false; } /** - * Delinks a node from the tree. + * Launches all the selected nodes of the tree * - * \param node the node to delink + * \param tree the tree for which all nodes will be launched */ -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->last_child == node) - node->parent->last_child = node->previous; - if (node->parent->child == NULL) { - /* don't contract top-level node */ - if (node->parent->parent) - 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; +void tree_launch_selected(struct tree *tree) +{ + if (tree->root->child != NULL) + tree_launch_selected_internal(tree, tree->root->child); } /** - * Deletes all selected node from the tree. + * Handles a mouse action for a tree * - * \param tree the tree to delete from - * \param node the node to delete + * \param tree the tree to handle a click for + * \param mouse the mouse state + * \param x X coordinate of mouse action + * \param y Y coordinate of mouse action + * \return whether the click was handled */ -void tree_delete_selected_nodes(struct tree *tree, struct node *node) { - struct node *next; +bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x, + int y) +{ + bool furniture; + struct node *node; + struct node *last; + struct node_element *element; + struct node_msg_data msg_data; - while (node) { - next = node->next; - if ((node->selected) && (node != tree->root)) - tree_delete_node(tree, node, false); - else if (node->child) - tree_delete_selected_nodes(tree, node->child); - node = next; + assert(tree != NULL); + assert(tree->root != NULL); + + if (tree->root->child == NULL) + return true; + + element = tree_get_node_element_at(tree->root->child, x, y, &furniture); + + /* pass in-textarea mouse action and drags which started in it + to the textarea */ + if (tree->editing != NULL) { + int x0, x1, y0, y1; + x0 = tree->editing->box.x; + if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) + x0 += NODE_INSTEP; + x1 = tree->editing->box.x + tree->editing->box.width; + y0 = tree->editing->box.y; + y1 = tree->editing->box.y + tree->editing->box.height; + + if (tree->textarea_drag_start && + (mouse & (BROWSER_MOUSE_HOLDING_1 | + BROWSER_MOUSE_HOLDING_2))) { + + textarea_mouse_action(tree->textarea, mouse, + x - x0, y - y0); + return true; + } + + + + if ((x >= x0) && (x < x1) && (y >= y0) && (y < y1)) { + + if (mouse & (BROWSER_MOUSE_DRAG_1 | + BROWSER_MOUSE_DRAG_2)) + tree->textarea_drag_start = true; + else + tree->textarea_drag_start = false; + textarea_mouse_action(tree->textarea, mouse, + x - x0, y - y0); + return true; + + } } -} + tree->textarea_drag_start = false; -/** - * 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) { - tree_delete_node_internal(tree, node, siblings); - if (tree->root) - tree_recalculate_node_positions(tree, tree->root); - tree_redraw_area(tree, 0, 0, 16384, 16384); /* \todo correct area */ - tree_recalculate_size(tree); + /* we are not interested in the drag path or in mouse presses, return */ + if (mouse & (BROWSER_MOUSE_HOLDING_1 | BROWSER_MOUSE_HOLDING_2 | + BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_PRESS_2)) + return true; + + /* cancel edit */ + if (tree->editing != NULL) + tree_stop_edit(tree, false); + + + + /* no item either means cancel selection on (select) click or a drag */ + if (element == NULL) { + if (tree->flags & TREE_SINGLE_SELECT) { + tree_set_node_selected(tree, tree->root->child, true, + false); + return true; + } + if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_DRAG_1)) + tree_set_node_selected(tree, tree->root->child, true, + false); + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { + + /** @todo the tree window has to scroll the tree when + * mouse reaches border while dragging this isn't + * solved for the browser window too. + */ + tree->drag = TREE_SELECT_DRAG; + } + return true; + } + + node = element->parent; + + /* click on furniture or double click on folder toggles node expansion + */ + if (((furniture) && (mouse & (BROWSER_MOUSE_CLICK_1 | + BROWSER_MOUSE_CLICK_2))) || + ((!furniture) && (node->child != NULL) && + (mouse & BROWSER_MOUSE_DOUBLE_CLICK))) { + + /* clear any selection */ + tree_set_node_selected(tree, tree->root->child, true, false); + + /* expand / contract node and redraw */ + tree_set_node_expanded(tree, node, !node->expanded, + false, false); + + /* find the last child node if expanded */ + last = node; + if ((last->child != NULL) && (last->expanded)) { + last = last->child; + while ((last->next != NULL) || + ((last->child != NULL) && + (last->expanded))) { + if (last->next != NULL) + last = last->next; + else + last = last->child; + } + } + /* scroll to the bottom element then back to the top */ + element = &last->data; + if (last->expanded) + for (; element->next != NULL; element = element->next); + tree->callbacks->scroll_visible(element->box.y, + element->box.height, + tree->client_data); + tree->callbacks->scroll_visible(node->data.box.y, + node->data.box.height, + tree->client_data); + return true; + } + + /* no use for any other furniture click */ + if (furniture) + return true; + + /* single/double ctrl+click or alt+click starts editing */ + if ((element->editable) && (!tree->editing) && + ((element->type == NODE_ELEMENT_TEXT) || + (element->type == NODE_ELEMENT_TEXT_PLUS_ICON)) && + (mouse & (BROWSER_MOUSE_CLICK_1 | + BROWSER_MOUSE_DOUBLE_CLICK)) && + (mouse & BROWSER_MOUSE_MOD_2 || + mouse & BROWSER_MOUSE_MOD_3)) { + tree_set_node_selected(tree, tree->root->child, true, false); + tree_start_edit(tree, element); + return true; + } + + /* double click launches the leaf */ + if (mouse & BROWSER_MOUSE_DOUBLE_CLICK) { + if (node->user_callback == NULL) + return false; + msg_data.msg = NODE_LAUNCH; + msg_data.flag = TREE_ELEMENT_TITLE; + msg_data.node = node; + if (node->user_callback(node->callback_data, &msg_data) != + NODE_CALLBACK_HANDLED) + return false; + + return true; + } + + /* single click (select) cancels current selection and selects item */ + if (mouse & BROWSER_MOUSE_CLICK_1 || (mouse & BROWSER_MOUSE_CLICK_2 && + tree->flags & TREE_SINGLE_SELECT)) { + if (tree->flags & TREE_NO_SELECT) + return true; + if (!node->selected) { + tree_set_node_selected(tree, tree->root->child, true, + false); + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + return true; + } + + /* single click (adjust) toggles item selection */ + if (mouse & BROWSER_MOUSE_CLICK_2) { + if (tree->flags & TREE_NO_SELECT) + return true; + node->selected = !node->selected; + tree_handle_node_element_changed(tree, &node->data); + return true; + } + + /* drag starts a drag operation */ + if ((!tree->editing) && (mouse & (BROWSER_MOUSE_DRAG_1 | + BROWSER_MOUSE_DRAG_2))) { + if (tree->flags & TREE_NO_DRAGS) + return true; + + if (!node->selected) { + tree_set_node_selected(tree, tree->root->child, true, + false); + node->selected = true; + tree_handle_node_element_changed(tree, &node->data); + } + + tree->drag = TREE_MOVE_DRAG; + + return true; + } + + + return false; } /** - * Deletes a node from the tree. + * Updates the selected state for a region of nodes. * - * \param tree the tree to delete from - * \param node the node to delete - * \param siblings whether to delete all siblings + * \param tree the tree to update + * \param node the node to update children and siblings of + * \param y the minimum y of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state */ -void tree_delete_node_internal(struct tree *tree, struct node *node, bool siblings) { - struct node *next, *child; - struct node_element *e, *f, *domain, *path; - const char *domain_t, *path_t, *name_t; - char *space; - - assert(node); - - if (tree->temp_selection == node) - tree->temp_selection = NULL; - if (tree->root == node) - tree->root = NULL; +static void tree_handle_selection_area_node(struct tree *tree, + struct node *node, int y, int height, bool invert) +{ + struct node_element *element; + struct node *update; + int y_max; + int y0, y1; - next = node->next; - tree_delink_node(node); - child = node->child; - node->child = NULL; - if (child) - tree_delete_node_internal(tree, child, true); + assert(tree != NULL); + assert(node != NULL); - if (!node->retain_in_memory) { - node->retain_in_memory = true; - for (e = &node->data; e; e = f) { - if (e->text) { - /* we don't free non-editable titles or URLs */ - if ((node->editable) || (node->folder)) - free((void *)e->text); - else { - /* only reset non-deleted items */ - if (!node->deleted) { - if (e->data == TREE_ELEMENT_URL) { - /* reset URL characteristics */ - urldb_reset_url_visit_data(e->text); - } else if (e->data == TREE_ELEMENT_NAME) { - /* get the rest of the cookie data */ - domain = tree_find_element(node, - TREE_ELEMENT_DOMAIN); - path = tree_find_element(node, - TREE_ELEMENT_PATH); - if (domain && path) { - domain_t = domain->text + - strlen(messages_get( - "TreeDomain")) - 4; - space = strchr(domain_t, ' '); - if (space) - *space = '\0'; - path_t = path->text + - strlen(messages_get( - "TreePath")) - 4; - space = strchr(path_t, ' '); - if (space) - *space = '\0'; - name_t = e->text; - urldb_delete_cookie( - domain_t, - path_t, - name_t); - } - } - } + y_max = y + height; - if (e->data != TREE_ELEMENT_TITLE && - e->data != TREE_ELEMENT_URL) { - free((void *)e->text); - e->text = NULL; + for (; node != NULL; node = node->next) { + if (node->box.y > y_max) return; + y0 = node->box.y; + y1 = node->box.y + node->box.height; + if ((y0 < y_max) && (y1 >= y)) { + update = NULL; + if (node->expanded) { + for (element = &node->data; element != NULL; + element = element->next) { + y0 = element->box.y; + y1 = element->box.y + + element->box.height; + if ((y0 < y_max) && (y1 >= y)) { + update = element->parent; + break; } } + } else { + y0 = node->data.box.y; + y1 = node->data.box.y + node->data.box.height; + if ((y0 < y_max) && (y1 >= y)) + update = node->data.parent; } - if (e->sprite) { - /* TODO the type of this field is platform dependent */ - free(e->sprite); /* \todo platform specific bits */ - e->sprite = NULL; + 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); + } } - f = e->next; - if (e != &node->data) - free(e); } - free(node); - } else { - node->deleted = true; + if ((node->child != NULL) && (node->expanded)) + tree_handle_selection_area_node(tree, node->child, y, + height, invert); } - if (siblings && next) - tree_delete_node_internal(tree, next, true); } + /** - * Creates a folder node with the specified title, and links it into the tree. + * Updates the selected state for a region of nodes. * - * \param parent the parent node, or NULL not to link - * \param title the node title (copied) - * \return the newly created node. + * \param tree the tree to update + * \param y the minimum y of the selection rectangle + * \param height the height of the selection rectangle + * \param invert whether to invert the selected state */ -struct node *tree_create_folder_node(struct node *parent, const char *title) { - struct node *node; +static void tree_handle_selection_area(struct tree *tree, int y, int height, + bool invert) +{ + assert(tree != NULL); + assert(tree->root != NULL); - assert(title); + if (tree->root->child == NULL) + return; - 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); - node->data.data = TREE_ELEMENT_TITLE; - tree_set_node_sprite_folder(node); - if (parent) - tree_link_node(parent, node, false); - return node; + if (height < 0) { + y += height; + height = -height; + } + tree_handle_selection_area_node(tree, tree->root->child, y, height, + invert); } /** - * Creates a leaf node with the specified title, and links it into the tree. + * Clears the processing flag. * - * \param parent the parent node, or NULL not to link - * \param title the node title (copied) - * \return the newly created node. + * \param node the node to process siblings and children of */ -struct node *tree_create_leaf_node(struct node *parent, const char *title) { - struct node *node; +static void tree_clear_processing(struct node *node) +{ + for (; node != NULL; node = node->next) { + node->processing = false; + if (node->child != NULL) + tree_clear_processing(node->child); + } +} - 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 = strdup(squash_whitespace(title)); - node->data.data = TREE_ELEMENT_TITLE; - node->editable = true; - if (parent) - tree_link_node(parent, node, false); - return node; +/** + * Sets the processing flag to the selection state. + * + * \param node the node to process siblings and children of + */ +static void tree_selected_to_processing(struct node *node) +{ + for (; node != NULL; node = node->next) { + node->processing = node->selected; + if ((node->child != NULL) && (node->expanded)) + tree_selected_to_processing(node->child); + } } /** - * Creates a leaf node with the specified title, and links it into the tree. + * Moves the first node in a tree with the processing flag set. * - * \param parent the parent node, or NULL not to link - * \param title the node title - * \return the newly created node. + * \param tree the tree in which the move takes place + * \param node 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_create_leaf_node_shared(struct node *parent, const char *title) { - struct node *node; - - assert(title); +static struct node *tree_move_processing_node(struct tree *tree, + struct node *node, struct node *link, bool before, bool first) +{ + struct node *result; - 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 = title; - node->data.data = TREE_ELEMENT_TITLE; - node->editable = false; - if (parent) - tree_link_node(parent, node, false); - return node; + bool folder = link->folder; + for (; node != NULL; node = node->next) { + if (node->processing) { + node->processing = false; + tree_delink_node(tree, node); + if (!first) + link->folder = false; + tree_link_node(tree, link, node, before); + if (!first) + link->folder = folder; + return node; + } + if (node->child != NULL) { + result = tree_move_processing_node(tree, node->child, + link, before, first); + if (result != NULL) + return result; + } + } + return NULL; } /** - * Creates a tree entry for a URL, and links it into the tree - * + * Moves nodes within a tree. * - * \param parent the node to link to - * \param url the URL (copied) - * \param data the URL data to use - * \param title the custom title to use - * \return the node created, or NULL for failure + * \param tree the tree to process + * \param destination 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 */ -struct node *tree_create_URL_node(struct node *parent, - const char *url, const struct url_data *data, - const char *title) { - struct node *node; - struct node_element *element; - - assert(data); +static void tree_move_selected_nodes(struct tree *tree, + struct node *destination, bool before) +{ + struct node *link; + struct node *test; + bool error; - node = tree_create_leaf_node(parent, title ? title : url); - if (!node) - return NULL; + tree_clear_processing(tree->root); + tree_selected_to_processing(tree->root); - element = tree_create_node_element(node, TREE_ELEMENT_THUMBNAIL); - if (element) - element->type = NODE_ELEMENT_THUMBNAIL; - tree_create_node_element(node, TREE_ELEMENT_VISITS); - tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT); - element = tree_create_node_element(node, TREE_ELEMENT_URL); - if (element) - element->text = strdup(url); + /* the destination node cannot be a child of any node with + the processing flag set */ + error = destination->processing; + for (test = destination; test != NULL; test = test->parent) + error |= test->processing; + if (error) { + tree_clear_processing(tree->root); + return; + } + if ((destination->folder) && (!destination->expanded) && (!before)) { + tree_set_node_expanded(tree, destination, true, false, false); + } + link = tree_move_processing_node(tree, tree->root, destination, before, + true); + while (link != NULL) + link = tree_move_processing_node(tree, tree->root, link, false, + false); - tree_update_URL_node(node, url, NULL); - return node; + tree_clear_processing(tree->root); + tree_recalculate_node_positions(tree, tree->root); + if (tree->redraw) + tree->callbacks->redraw_request(0, 0, tree->width, tree->height, + tree->client_data); } /** - * Creates a tree entry for a URL, and links it into the tree. - * - * All information is used directly from the url_data, and as such cannot be - * edited and should never be freed. + * Handle the end of a drag operation * - * \param parent the node to link to - * \param url the URL - * \param data the URL data to use - * \return the node created, or NULL for failure + * \param tree the tree on which the drag was performed + * \param mouse mouse state during drag end + * \param x0 x coordinate of drag start + * \param y0 y coordinate of drag start + * \param x1 x coordinate of drag end + * \param y1 y coordinate of drag end */ -struct node *tree_create_URL_node_shared(struct node *parent, - const char *url, const struct url_data *data) { - struct node *node; - struct node_element *element; - const char *title; +void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0, + int x1, int y1) +{ - assert(url && data); - - if (data->title) - title = data->title; - else - title = url; - node = tree_create_leaf_node_shared(parent, title); - if (!node) - return NULL; + bool before; + struct node *node; + int x, y; + + if (tree->textarea_drag_start) { + x = tree->editing->box.x; + y = tree->editing->box.y; + if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) + x += NODE_INSTEP; + textarea_drag_end(tree->textarea, mouse, x1 - x, y1 - y); + } - element = tree_create_node_element(node, TREE_ELEMENT_THUMBNAIL); - if (element) - element->type = NODE_ELEMENT_THUMBNAIL; - tree_create_node_element(node, TREE_ELEMENT_VISITS); - tree_create_node_element(node, TREE_ELEMENT_LAST_VISIT); - element = tree_create_node_element(node, TREE_ELEMENT_URL); - if (element) - element->text = url; + tree->textarea_drag_start = false; + + switch (tree->drag) { + case TREE_NO_DRAG: + break; + case TREE_SELECT_DRAG: + tree_handle_selection_area(tree, y0, y1 - y0, + (mouse | BROWSER_MOUSE_HOLDING_2)); + break; + case TREE_MOVE_DRAG: + if (!(tree->flags & TREE_MOVABLE)) + return; + node = tree_get_link_details(tree, x1, y1, &before); + tree_move_selected_nodes(tree, node, before); + break; + } - tree_update_URL_node(node, url, data); - return node; + tree->drag = TREE_NO_DRAG; } /** - * Creates a tree entry for a cookie, and links it into the tree. - * - * All information is copied from the cookie_data, and as such can - * be edited and should be freed. + * Key press handling for a tree. * - * \param parent the node to link to - * \param url the URL - * \param data the cookie data to use - * \return the node created, or NULL for failure + * \param tree The tree which got the keypress + * \param key The ucs4 character codepoint + * \return true if the keypress is dealt with, false otherwise. */ -struct node *tree_create_cookie_node(struct node *parent, - const struct cookie_data *data) { - struct node *node; - struct node_element *element; - char buffer[256]; - char buffer2[16]; - - node = tree_create_leaf_node(parent, data->name); - if (!node) - return NULL; - node->data.data = TREE_ELEMENT_NAME; - node->editable = false; - +bool tree_keypress(struct tree *tree, uint32_t key) +{ - element = tree_create_node_element(node, TREE_ELEMENT_PERSISTENT); - if (element) { - snprintf(buffer, 256, messages_get("TreePersistent"), - data->no_destroy ? messages_get("Yes") : messages_get("No")); - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_VERSION); - if (element) { - snprintf(buffer2, 16, "TreeVersion%i", data->version); - snprintf(buffer, 256, messages_get("TreeVersion"), messages_get(buffer2)); - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_SECURE); - if (element) { - snprintf(buffer, 256, messages_get("TreeSecure"), - data->secure ? messages_get("Yes") : messages_get("No")); - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_LAST_USED); - if (element) { - snprintf(buffer, 256, messages_get("TreeLastUsed"), - (data->last_used > 0) ? - ctime(&data->last_used) : messages_get("TreeUnknown")); - if (data->last_used > 0) - buffer[strlen(buffer) - 1] = '\0'; - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_EXPIRES); - if (element) { - snprintf(buffer, 256, messages_get("TreeExpires"), - (data->expires > 0) - ? (data->expires == 1) - ? messages_get("TreeSession") - : ctime(&data->expires) - : messages_get("TreeUnknown")); - if (data->expires > 0 && data->expires != 1) - buffer[strlen(buffer) - 1] = '\0'; - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_PATH); - if (element) { - snprintf(buffer, 256, messages_get("TreePath"), data->path, - data->path_from_set ? messages_get("TreeHeaders") : ""); - element->text = strdup(buffer); - } - element = tree_create_node_element(node, TREE_ELEMENT_DOMAIN); - if (element) { - snprintf(buffer, 256, messages_get("TreeDomain"), data->domain, - data->domain_from_set ? messages_get("TreeHeaders") : ""); - element->text = strdup(buffer); - } - if ((data->comment) && (strcmp(data->comment, ""))) { - element = tree_create_node_element(node, TREE_ELEMENT_COMMENT); - if (element) { - snprintf(buffer, 256, messages_get("TreeComment"), data->comment); - element->text = strdup(buffer); + if (tree->editing != NULL) + switch (key) { + case KEY_ESCAPE: + tree_stop_edit(tree, false); + return true; + case KEY_NL: + tree_stop_edit(tree, true); + return true; + default: + return textarea_keypress(tree->textarea, key); } - } - element = tree_create_node_element(node, TREE_ELEMENT_VALUE); - if (element) { - snprintf(buffer, 256, messages_get("TreeValue"), - data->value ? data->value : messages_get("TreeUnused")); - element->text = strdup(buffer); - } - tree_set_node_sprite(node, "small_xxx", "small_xxx"); - return node; + return false; } /** - * Creates an empty text node element and links it to a node. + * Alphabetical comparison function for nodes * - * \param parent the parent node - * \param user_type the required user_type - * \return the newly created element. + * \param n1 first node to compare + * \param n2 first node to compare + * \return 0 if equal, greater then zero if n1 > n2, + * less then zero if n2 < n1 */ -struct node_element *tree_create_node_element(struct node *parent, node_element_data data) { - struct node_element *element; +int tree_alphabetical_sort(struct node *n1, struct node *n2) +{ + return strcmp(n1->data.text, n2->data.text); +} - element = calloc(sizeof(struct node_element), 1); - if (!element) return NULL; - element->parent = parent; - element->data = data; - element->type = NODE_ELEMENT_TEXT; - element->next = parent->data.next; - parent->data.next = element; - return element; + +/** + * Redraw requests from the textarea are piped through this because we have to + * check the redraw flag of the tree before requesting a redraw and change the + * position to tree origin relative. + */ +static void tree_textarea_redraw_request(void *data, int x, int y, + int width, int height) +{ + struct tree *tree = data; + x = x + tree->editing->box.x; + y = y + tree->editing->box.y; + if (tree->editing->type == NODE_ELEMENT_TEXT_PLUS_ICON) + x += NODE_INSTEP; + + if (tree->redraw) + tree->callbacks->redraw_request(x, y, + width, height, + tree->client_data); } /** - * Recalculates the size of a tree. + * Starts editing a node_element * - * \param tree the tree to recalculate + * \param tree The tree to which element belongs + * \param element The element to start being edited */ -void tree_recalculate_size(struct tree *tree) { +void tree_start_edit(struct tree *tree, struct node_element *element) +{ + struct node *parent; int width, height; - assert(tree); + assert(tree != NULL); + assert(element != NULL); + + if (tree->editing != NULL) + tree_stop_edit(tree, true); + + parent = element->parent; + if (&parent->data == element) + parent = parent->parent; + for (; parent != NULL; parent = parent->parent) { + if (!parent->expanded) { + tree_set_node_expanded(tree, parent, true, + false, false); + } + } + + tree->editing = element; + tree->callbacks->get_window_dimensions(&width, NULL, tree->client_data); + width -= element->box.x; + height = element->box.height; + if (element->type == NODE_ELEMENT_TEXT_PLUS_ICON) + width -= NODE_INSTEP; - if (!tree->handle) + tree->textarea = textarea_create(width, height, 0, + &plot_fstyle, tree_textarea_redraw_request, tree); + if (tree->textarea == NULL) { + tree_stop_edit(tree, false); return; - width = tree->width; - height = tree->height; - if (tree->root) { - tree->width = tree_get_node_width(tree->root); - tree->height = tree_get_node_height(tree->root); - } else { - tree->width = 0; - tree->height = 0; } - if ((width != tree->width) || (height != tree->height)) - tree_resized(tree); + textarea_set_text(tree->textarea, element->text); + + tree_handle_node_element_changed(tree, element); + tree_recalculate_size(tree); + tree->callbacks->scroll_visible(element->box.y, element->box.height, + tree->client_data); } /** - * 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 + * Callback for fetchcache(). Should be removed once bitmaps get loaded directly + * from disc */ -struct node *tree_get_selected_node(struct node *node) { - struct node *result = NULL; - struct node *temp; +static nserror tree_icon_callback(hlcache_handle *handle, + const hlcache_event *event, void *pw) +{ + return NSERROR_OK; +} - 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; - } + +/** + * Tree utility function. Placed here so that this code doesn't have to be + * copied by each user. + * + * \param name the name of the loaded icon, if it's not a full path the icon is + * looked for in the directory specified by option_tree_icons_dir + * \return the icon in form of a content or NULL on failure + */ +hlcache_handle *tree_load_icon(const char *name) +{ + char *url = NULL; + const char *icon_url = NULL; + int len; + hlcache_handle *c; + nserror err; + + /** @todo something like bitmap_from_disc is needed here */ + + if (!strncmp(name, "file://", 7)) { + icon_url = name; + } else { + char *native_path; + + if (option_tree_icons_dir == NULL) + return NULL; + + /* path + separator + leafname + '\0' */ + len = strlen(option_tree_icons_dir) + 1 + strlen(name) + 1; + native_path = malloc(len); + if (native_path == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; } + + /* Build native path */ + memcpy(native_path, option_tree_icons_dir, + strlen(option_tree_icons_dir) + 1); + path_add_part(native_path, len, name); + + /* Convert native path to URL */ + url = path_to_url(native_path); + + free(native_path); + icon_url = url; } - return result; + + /* Fetch the icon */ + err = hlcache_handle_retrieve(icon_url, 0, 0, 0, + tree_icon_callback, 0, 0, 0, &c); + + + /* If we built the URL here, free it */ + if (url != NULL) + free(url); + + if (err != NSERROR_OK) { + return NULL; + } + + return c; } diff --git a/desktop/tree.h b/desktop/tree.h index d02f1726e..707f3d126 100644 --- a/desktop/tree.h +++ b/desktop/tree.h @@ -1,5 +1,6 @@ /* * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -26,157 +27,167 @@ #include <stdbool.h> #include <stdint.h> -struct url_data; -struct cookie_data; - -typedef enum { - TREE_ELEMENT_URL, - TREE_ELEMENT_ADDED, - TREE_ELEMENT_LAST_VISIT, - TREE_ELEMENT_VISITS, - TREE_ELEMENT_VISITED, - TREE_ELEMENT_THUMBNAIL, - TREE_ELEMENT_TITLE, - TREE_ELEMENT_NAME, - TREE_ELEMENT_VALUE, - TREE_ELEMENT_COMMENT, - TREE_ELEMENT_DOMAIN, - TREE_ELEMENT_PATH, - TREE_ELEMENT_EXPIRES, - TREE_ELEMENT_LAST_USED, - TREE_ELEMENT_SECURE, - TREE_ELEMENT_VERSION, - TREE_ELEMENT_PERSISTENT, - TREE_ELEMENT_SSL -} node_element_data; - -#define NODE_INSTEP 40 - -struct node_sprite; -struct toolbar; - -typedef enum { - NODE_ELEMENT_TEXT, /* <-- Text only */ - NODE_ELEMENT_TEXT_PLUS_SPRITE, /* <-- Text and sprite */ - NODE_ELEMENT_THUMBNAIL, /* <-- Bitmap 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 */ +#include "desktop/browser.h" +#include "image/bitmap.h" + +struct hlcache_handle; + +/* Tree flags */ +enum tree_flags { + TREE_NO_FLAGS = 0, + TREE_NO_DRAGS = 1, + TREE_NO_FURNITURE = 2, + TREE_SINGLE_SELECT = 4, + TREE_NO_SELECT = 8, + TREE_MOVABLE = 16, + TREE_DELETE_EMPTY_DIRS = 32, /**< if the last child of a + * directory is deleted the + * directory will be deleted + * too. + */ }; +/** A "flag" value to indicate the element data contains title + * text. This value should be the first node_element in every + * node. All other values should be different than this one. The term + * flag is misused as it is actually a value used by the API consumer + * to indicate teh type of data a node element contains. + */ +#define TREE_ELEMENT_TITLE 0x00 -struct node_element { - struct node *parent; /* <-- Parent node */ - node_element_type type; /* <-- Element type */ - struct node_element_box box; /* <-- Element bounding box */ - const char *text; /* <-- Text for the element */ - struct node_sprite *sprite; /* <-- Sprite for the element */ - struct node_element *next; /* <-- Next node element */ - node_element_data data; /* <-- Data being represented */ -}; +/* these should be defined in front end code */ +extern const char tree_directory_icon_name[]; +extern const char tree_content_icon_name[]; + +struct tree; +struct node; +struct node_element; +typedef enum { + NODE_ELEMENT_TEXT, /**< Text only */ + NODE_ELEMENT_TEXT_PLUS_ICON, /**< Text and icon */ + NODE_ELEMENT_BITMAP /**< Bitmap only */ +} node_element_type; -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 retain_in_memory; /* <-- Whether the node remains in memory after deletion */ - bool deleted; /* <-- Whether the node is currently deleted */ - 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 *last_child; /* <-- Last child */ - struct node *previous; /* <-- Previous child of the parent */ - struct node *next; /* <-- Next child of the parent */ +typedef enum { + NODE_DELETE_ELEMENT_TXT, /**< The text of an element of the + * node is being deleted */ + NODE_DELETE_ELEMENT_IMG, /**< The bitmap or icon of a node is + * being deleted */ + NODE_LAUNCH, /**< The node has been launched */ + NODE_ELEMENT_EDIT_FINISHING, /**< New text has to be accepted + * or rejected. */ + NODE_ELEMENT_EDIT_FINISHED /**< Editing of a node_element has + * been finished. */ +} node_msg; +typedef enum { + NODE_CALLBACK_HANDLED, + NODE_CALLBACK_NOT_HANDLED, + NODE_CALLBACK_REJECT, /**< reject new text for node element + * and leave editing mode. */ + NODE_CALLBACK_CONTINUE /**< don't leave editig mode. */ +} node_callback_resp; + +/** Internal node message. */ +struct node_msg_data { + node_msg msg; /**< The type of message. */ + unsigned int flag; /**< message flags. */ + struct node *node; /**< tree node messsage concerns. */ + union { + char *text; /**< textural data. */ + void *bitmap; /**< bitmap data. */ + } data; /**< The message data. */ }; -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 */ - bool no_drag; /* <-- Tree items can't be dragged out */ - bool no_vscroll; /* <-- Tree has a vertical scroll only when needed */ - bool no_furniture; /* <-- Tree does not have connecting lines */ - bool single_selection; /* <-- There can only be one item selected */ - int edit_handle; /* <-- Handle for editing information */ - uintptr_t textarea_handle; /* <-- Handle for UTF-8 textarea */ - bool movable; /* <-- Whether nodes can be moved */ - struct node_element *editing; /* <-- Node element being edited */ - struct node *temp_selection; /* <-- Temporarily selected node */ - struct toolbar *toolbar; /* <-- Tree toolbar */ +/** callbacks to perform necessary operations on treeview. */ +struct treeview_table { + void (*redraw_request)(int x, int y, int width, int height, + void *data); /**< request a redraw. */ + void (*resized)(struct tree *tree, int width, int height, + void *data); /**< resize treeview area. */ + void (*scroll_visible)(int y, int height, void *data); /**< scroll visible treeview area. */ + void (*get_window_dimensions)(int *width, int *height, void *data); /**< get dimensions of window */ }; +/** + * Informs the client about any events requiring his action + * + * \param user_data the user data which was passed at tree creation + * \param msg_data structure containing all the message information + * \return the appropriate node_callback_resp response + */ +typedef node_callback_resp (*tree_node_user_callback)(void *user_data, + struct node_msg_data *msg_data); /* Non-platform specific code */ -void tree_initialise(struct tree *tree); -void tree_initialise_nodes(struct tree *tree, 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); -void tree_recalculate_node(struct tree *tree, struct node *node, bool recalculate_sizes); -void tree_recalculate_node_positions(struct tree *tree, struct node *root); -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, node_element_data data); -void tree_move_selected_nodes(struct tree *tree, struct node *destination, + +/* Functions for creating/deleting tree primitives and for tree structure + manipulation */ +struct tree *tree_create(unsigned int flags, + const struct treeview_table *callbacks, + void *client_data); +struct node *tree_create_folder_node(struct tree *tree, struct node *parent, + const char *title, bool editable, bool retain_in_memory, + bool deleted); +struct node *tree_create_leaf_node(struct tree *tree, struct node *parent, + const char *title, bool editable, bool retain_in_memory, + bool deleted); +struct node_element *tree_create_node_element(struct node *parent, + node_element_type type, unsigned int flag, bool editable); +void tree_link_node(struct tree *tree, struct node *link, struct node *node, 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); -struct node *tree_create_URL_node(struct node *parent, - const char *url, const struct url_data *data, - const char *title); -struct node *tree_create_URL_node_shared(struct node *parent, - const char *url, const struct url_data *data); -struct node *tree_create_cookie_node(struct node *parent, - const struct cookie_data *data); -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded); -void tree_set_node_expanded(struct tree *tree, 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_delink_node(struct tree *tree, struct node *node); +void tree_delete(struct tree *tree); 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, + +/* setters and getters for properties and data */ +void tree_set_node_icon(struct tree *tree, struct node *node, + struct hlcache_handle *icon); +void tree_set_node_expanded(struct tree *tree, struct node *node, bool expanded, bool folder, bool leaf); +void tree_set_node_selected(struct tree *tree, struct node *node, bool all, + bool selected); +void tree_set_node_sort_function(struct tree *tree, struct node *node, + int (*sort) (struct node *, struct node *)); +void tree_set_node_user_callback(struct node *node, + tree_node_user_callback callback, void *data); +void tree_set_redraw(struct tree *tree, bool redraw); +bool tree_get_redraw(struct tree *tree); +bool tree_node_has_selection(struct node *node); +bool tree_node_is_deleted(struct node *node); +bool tree_node_is_folder(struct node *node); +void tree_update_node_element(struct tree *tree, struct node_element *element, + const char *text, void *bitmap); +bool tree_update_element_text(struct tree *tree, struct node_element *element, char *text); +const char *tree_node_element_get_text(struct node_element *element); +struct node *tree_get_root(struct tree *tree); +bool tree_is_edited(struct tree *tree); + + +/* functions for traversing the tree */ +struct node *tree_node_get_child(struct node *node); +struct node *tree_node_get_next(struct node *node); + +void tree_draw(struct tree *tree, int x, int y, + int clip_x, int clip_y, int clip_width, int clip_height); + +struct node_element *tree_node_find_element(struct node *node, + unsigned int flag, struct node_element *after); +void tree_delete_selected_nodes(struct tree *tree, struct node *node); 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(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, const char *url, - const struct url_data *data); -void tree_resized(struct tree *tree); -void tree_set_node_sprite_folder(struct node *node); - +void tree_launch_selected(struct tree *tree); + +bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, + int x, int y); +void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int y0, + int x1, int y1); +bool tree_keypress(struct tree *tree, uint32_t key); + +int tree_alphabetical_sort(struct node *, struct node *); +void tree_start_edit(struct tree *tree, struct node_element *element); +struct hlcache_handle *tree_load_icon(const char *name); + #endif diff --git a/desktop/tree_url_node.c b/desktop/tree_url_node.c new file mode 100644 index 000000000..3bc9f90fc --- /dev/null +++ b/desktop/tree_url_node.c @@ -0,0 +1,846 @@ +/* + * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Creation of URL nodes with use of trees (implementation) + */ + + +#include <assert.h> +#include <ctype.h> +#include <libxml/HTMLparser.h> +#include <libxml/HTMLtree.h> + +#include "content/content.h" +#include "content/hlcache.h" +#include "content/urldb.h" +#include "desktop/browser.h" +#include "desktop/options.h" +#include "desktop/tree_url_node.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/url.h" +#include "utils/utils.h" + +/** Flags for each type of url tree node. */ +enum tree_element_url { + TREE_ELEMENT_URL = 0x01, + TREE_ELEMENT_LAST_VISIT = 0x02, + TREE_ELEMENT_VISITS = 0x03, + TREE_ELEMENT_THUMBNAIL = 0x04, +}; + +#define MAX_ICON_NAME_LEN 256 + +static bool initialised = false; + +static hlcache_handle *folder_icon; + +struct icon_entry { + content_type type; + hlcache_handle *icon; +}; + +struct icon_entry icon_table[] = { + {CONTENT_HTML, NULL}, + {CONTENT_TEXTPLAIN, NULL}, + {CONTENT_CSS, NULL}, +#if defined(WITH_MNG) || defined(WITH_PNG) + {CONTENT_PNG, NULL}, +#endif +#ifdef WITH_MNG + {CONTENT_JNG, NULL}, + {CONTENT_MNG, NULL}, +#endif +#ifdef WITH_JPEG + {CONTENT_JPEG, NULL}, +#endif +#ifdef WITH_GIF + {CONTENT_GIF, NULL}, +#endif +#ifdef WITH_BMP + {CONTENT_BMP, NULL}, + {CONTENT_ICO, NULL}, +#endif +#ifdef WITH_SPRITE + {CONTENT_SPRITE, NULL}, +#endif +#ifdef WITH_DRAW + {CONTENT_DRAW, NULL}, +#endif +#ifdef WITH_ARTWORKS + {CONTENT_ARTWORKS, NULL}, +#endif +#ifdef WITH_NS_SVG + {CONTENT_SVG, NULL}, +#endif + {CONTENT_UNKNOWN, NULL}, + + /* this serves as a sentinel */ + {CONTENT_HTML, NULL} +}; + + +void tree_url_node_init(void) +{ + struct icon_entry *entry; + char icon_name[MAX_ICON_NAME_LEN]; + + if (initialised || option_tree_icons_dir == NULL) + return; + initialised = true; + + folder_icon = tree_load_icon(tree_directory_icon_name); + + entry = icon_table; + do { + + tree_icon_name_from_content_type(icon_name, entry->type); + entry->icon = tree_load_icon(icon_name); + + ++entry; + } while (entry->type != CONTENT_HTML); + +} + + +/** + * Creates a tree entry for a URL, and links it into the tree + * + * \param parent the node to link to + * \param url the URL (copied) + * \param data the URL data to use + * \param title the custom title to use + * \return the node created, or NULL for failure + */ +struct node *tree_create_URL_node(struct tree *tree, struct node *parent, + const char *url, const char *title, + tree_node_user_callback user_callback, void *callback_data) +{ + struct node *node; + struct node_element *element; + char *text_cp, *squashed; + + squashed = squash_whitespace(title ? title : url); + text_cp = strdup(squashed); + if (text_cp == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + free(squashed); + node = tree_create_leaf_node(tree, parent, text_cp, true, false, + false); + if (node == NULL) { + free(text_cp); + return NULL; + } + + if (user_callback != NULL) + tree_set_node_user_callback(node, user_callback, + callback_data); + + tree_create_node_element(node, NODE_ELEMENT_BITMAP, + TREE_ELEMENT_THUMBNAIL, false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, TREE_ELEMENT_VISITS, + false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_LAST_VISIT, false); + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_URL, true); + if (element != NULL) { + text_cp = strdup(url); + if (text_cp == NULL) { + tree_delete_node(tree, node, false); + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + tree_update_node_element(tree, element, text_cp, NULL); + } + + return node; +} + + +/** + * Creates a tree entry for a URL, and links it into the tree. + * + * All information is used directly from the url_data, and as such cannot be + * edited and should never be freed. + * + * \param parent the node to link to + * \param url the URL + * \param data the URL data to use + * \return the node created, or NULL for failure + */ +struct node *tree_create_URL_node_shared(struct tree *tree, struct node *parent, + const char *url, const struct url_data *data, + tree_node_user_callback user_callback, void *callback_data) +{ + struct node *node; + struct node_element *element; + const char *title; + + assert(url && data); + + if (data->title != NULL) { + title = data->title; + } else { + title = url; + } + + node = tree_create_leaf_node(tree, parent, title, false, false, false); + if (node == NULL) + return NULL; + + if (user_callback != NULL) { + tree_set_node_user_callback(node, user_callback, + callback_data); + } + + tree_create_node_element(node, NODE_ELEMENT_BITMAP, + TREE_ELEMENT_THUMBNAIL, false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, TREE_ELEMENT_VISITS, + false); + tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_LAST_VISIT, false); + element = tree_create_node_element(node, NODE_ELEMENT_TEXT, + TREE_ELEMENT_URL, false); + if (element != NULL) { + tree_update_node_element(tree, element, url, NULL); + } + + tree_update_URL_node(tree, node, url, data, true); + return node; +} + + +/** + * Updates the node details for a URL node. + * + * \param node the node to update + */ +void tree_update_URL_node(struct tree *tree, struct node *node, + const char *url, const struct url_data *data, bool shared) +{ + struct node_element *element; + struct bitmap *bitmap = NULL; + struct icon_entry *entry; + char *text_cp; + + assert(node != NULL); + + element = tree_node_find_element(node, TREE_ELEMENT_URL, NULL); + if (element == NULL) + return; + + if (data != NULL) { + if (data->title == NULL) + urldb_set_url_title(url, url); + + if (data->title == NULL) + return; + + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, + NULL); + if (shared) + tree_update_node_element(tree, element, data->title, + NULL); + else { + text_cp = strdup(data->title); + if (text_cp == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return; + } + tree_update_node_element(tree, element, text_cp, NULL); + } + } else { + data = urldb_get_url_data(url); + if (data == NULL) + return; + } + + entry = icon_table; + do { + if (entry->type == data->type) { + if (entry->icon != NULL) + tree_set_node_icon(tree, node, entry->icon); + break; + } + ++entry; + } while (entry->type != CONTENT_HTML); + + /* update last visit text */ + element = tree_node_find_element(node, TREE_ELEMENT_LAST_VISIT, element); + tree_update_element_text(tree, + element, + messages_get_buff("TreeLast", + (data->last_visit > 0) ? + ctime((time_t *)&data->last_visit) : + messages_get("TreeUnknown"))); + + + /* update number of visits text */ + element = tree_node_find_element(node, TREE_ELEMENT_VISITS, element); + tree_update_element_text(tree, + element, + messages_get_buff("TreeVisits", data->visits)); + + + /* update thumbnail */ + element = tree_node_find_element(node, TREE_ELEMENT_THUMBNAIL, element); + if (element != NULL) { + bitmap = urldb_get_thumbnail(url); + + if (bitmap != NULL) { + tree_update_node_element(tree, element, NULL, bitmap); + } + } +} + + +const char *tree_url_node_get_title(struct node *node) +{ + struct node_element *element; + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + if (element == NULL) + return NULL; + return tree_node_element_get_text(element); +} + + +const char *tree_url_node_get_url(struct node *node) +{ + struct node_element *element; + element = tree_node_find_element(node, TREE_ELEMENT_URL, NULL); + if (element == NULL) + return NULL; + return tree_node_element_get_text(element); +} + +void tree_url_node_edit_title(struct tree *tree, struct node *node) +{ + struct node_element *element; + element = tree_node_find_element(node, TREE_ELEMENT_TITLE, NULL); + tree_start_edit(tree, element); +} + +void tree_url_node_edit_url(struct tree *tree, struct node *node) +{ + struct node_element *element; + element = tree_node_find_element(node, TREE_ELEMENT_URL, NULL); + tree_start_edit(tree, element); +} + +node_callback_resp tree_url_node_callback(void *user_data, + struct node_msg_data *msg_data) +{ + struct tree *tree; + struct node_element *element; + url_func_result res; + const char *text; + char *norm_text, *escaped_text; + const struct url_data *data; + + /** @todo memory leaks on non-shared folder deletion. */ + switch (msg_data->msg) { + case NODE_DELETE_ELEMENT_TXT: + switch (msg_data->flag) { + /* only history is using non-editable url + * elements so only history deletion will run + * this code + */ + case TREE_ELEMENT_URL: + /* reset URL characteristics */ + urldb_reset_url_visit_data( + msg_data->data.text); + return NODE_CALLBACK_HANDLED; + case TREE_ELEMENT_TITLE: + return NODE_CALLBACK_HANDLED; + } + break; + case NODE_DELETE_ELEMENT_IMG: + if (msg_data->flag == TREE_ELEMENT_THUMBNAIL || + msg_data->flag == TREE_ELEMENT_TITLE) + return NODE_CALLBACK_HANDLED; + break; + case NODE_LAUNCH: + element = tree_node_find_element(msg_data->node, + TREE_ELEMENT_URL, NULL); + if (element != NULL) { + text = tree_node_element_get_text(element); + browser_window_create(text, NULL, 0, + true, false); + return NODE_CALLBACK_HANDLED; + } + break; + case NODE_ELEMENT_EDIT_FINISHING: + + text = msg_data->data.text; + + if (msg_data->flag == TREE_ELEMENT_URL) { + res = url_escape(text, 0, false, NULL, + &escaped_text); + if (res == URL_FUNC_OK) + res = url_normalize(escaped_text, + &norm_text); + if (res != URL_FUNC_OK) { + if (res == URL_FUNC_FAILED) { + warn_user("NoURLError", 0); + return NODE_CALLBACK_CONTINUE; + } + else { + warn_user("NoMemory", 0); + return NODE_CALLBACK_REJECT; + } + + } + msg_data->data.text = norm_text; + + data = urldb_get_url_data(norm_text); + if (data == NULL) { + urldb_add_url(norm_text); + urldb_set_url_persistence(norm_text, + true); + data = urldb_get_url_data(norm_text); + if (data == NULL) + return NODE_CALLBACK_REJECT; + } + tree = user_data; + tree_update_URL_node(tree, msg_data->node, + norm_text, NULL, false); + } + else if (msg_data->flag == TREE_ELEMENT_TITLE) { + while (isspace(*text)) + text++; + norm_text = strdup(text); + if (norm_text == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NODE_CALLBACK_REJECT; + } + /* don't allow zero length entry text, return + false */ + if (norm_text[0] == '\0') { + warn_user("NoNameError", 0); + msg_data->data.text = NULL; + return NODE_CALLBACK_CONTINUE; + } + msg_data->data.text = norm_text; + } + + return NODE_CALLBACK_HANDLED; + default: + break; + } + return NODE_CALLBACK_NOT_HANDLED; +} + +/** + * 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 + */ +static xmlNode *tree_url_find_xml_element(xmlNode *node, const char *name) +{ + xmlNode *xmlnode; + if (node == NULL) + return NULL; + + for (xmlnode = node->children; + xmlnode && !(xmlnode->type == XML_ELEMENT_NODE && + strcmp((const char *) xmlnode->name, name) == 0); + xmlnode = xmlnode->next) + ; + + return xmlnode; +} + +/** + * Parse an entry represented as a li. + * + * \param li xmlNode for parsed li + * \param directory directory to add this entry to + */ +static void tree_url_load_entry(xmlNode *li, struct tree *tree, + struct node *directory, tree_node_user_callback callback, + void *callback_data) +{ + char *url = NULL, *url1 = NULL; + char *title = NULL; + struct node *entry; + xmlNode *xmlnode; + const struct url_data *data; + url_func_result res; + + for (xmlnode = li->children; xmlnode; xmlnode = xmlnode->next) { + /* The li must contain an "a" element */ + if (xmlnode->type == XML_ELEMENT_NODE && + strcmp((const char *)xmlnode->name, "a") == 0) { + url1 = (char *)xmlGetProp(xmlnode, (const xmlChar *) "href"); + title = (char *)xmlNodeGetContent(xmlnode); + } + } + + if ((url1 == NULL) || (title == NULL)) { + warn_user("TreeLoadError", "(Missing <a> in <li> or " + "memory exhausted.)"); + return; + } + + /* We're loading external input. + * This may be garbage, so attempt to normalise + */ + res = url_normalize(url1, &url); + if (res != URL_FUNC_OK) { + LOG(("Failed normalising '%s'", url1)); + + if (res == URL_FUNC_NOMEM) + warn_user("NoMemory", NULL); + + xmlFree(url1); + xmlFree(title); + + return; + } + + /* No longer need this */ + xmlFree(url1); + + data = urldb_get_url_data(url); + if (data == NULL) { + /* No entry in database, so add one */ + urldb_add_url(url); + /* now attempt to get url data */ + data = urldb_get_url_data(url); + } + if (data == NULL) { + xmlFree(title); + free(url); + + return; + } + + /* Make this URL persistent */ + urldb_set_url_persistence(url, true); + + if (data->title == NULL) + urldb_set_url_title(url, title); + + entry = tree_create_URL_node(tree, directory, url, title, + callback, callback_data); + + if (entry == NULL) { + /** \todo why isn't this fatal? */ + warn_user("NoMemory", 0); + } else { + tree_update_URL_node(tree, entry, url, data, false); + } + + + xmlFree(title); + free(url); +} + +/** + * Parse a directory represented as a ul. + * + * \param ul xmlNode for parsed ul + * \param directory directory to add this directory to + */ +static void tree_url_load_directory(xmlNode *ul, struct tree *tree, + struct node *directory, tree_node_user_callback callback, + void *callback_data) +{ + char *title; + struct node *dir; + xmlNode *xmlnode; + + assert(ul != NULL); + assert(directory != NULL); + + for (xmlnode = ul->children; xmlnode; xmlnode = xmlnode->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 (xmlnode->type != XML_ELEMENT_NODE) + continue; + + if (strcmp((const char *)xmlnode->name, "li") == 0) { + /* entry */ + tree_url_load_entry(xmlnode, tree, directory, callback, + callback_data); + + } else if (strcmp((const char *)xmlnode->name, "h4") == 0) { + /* directory */ + title = (char *) xmlNodeGetContent(xmlnode ); + if (!title) { + warn_user("TreeLoadError", "(Empty <h4> " + "or memory exhausted.)"); + return; + } + + for (xmlnode = xmlnode->next; + xmlnode && xmlnode->type != XML_ELEMENT_NODE; + xmlnode = xmlnode->next) + ; + if ((xmlnode == NULL) || + strcmp((const char *)xmlnode->name, "ul") != 0) { + /* next element isn't expected ul */ + free(title); + warn_user("TreeLoadError", "(Expected " + "<ul> not present.)"); + return; + } + + dir = tree_create_folder_node(tree, directory, title, + true, false, false); + if (dir == NULL) { + free(title); + return; + } + + if (callback != NULL) + tree_set_node_user_callback(dir, callback, + callback_data); + + if (folder_icon != NULL) + tree_set_node_icon(tree, dir, folder_icon); + + tree_url_load_directory(xmlnode, tree, dir, callback, + callback_data); + } + } +} + +/** + * Loads an url tree from a specified file. + * + * \param filename name of file to read + * \param tree empty tree which data will be read into + * \return the file represented as a tree, or NULL on failure + */ +bool tree_urlfile_load(const char *filename, struct tree *tree, + tree_node_user_callback callback, void *callback_data) +{ + xmlDoc *doc; + xmlNode *html, *body, *ul; + struct node *root; + FILE *fp = NULL; + + if (filename == NULL) { + return false; + } + + fp = fopen(filename, "r"); + if (fp == NULL) { + return false; + } + fclose(fp); + + doc = htmlParseFile(filename, "iso-8859-1"); + if (doc == NULL) { + warn_user("TreeLoadError", messages_get("ParsingFail")); + return false; + } + + html = tree_url_find_xml_element((xmlNode *) doc, "html"); + body = tree_url_find_xml_element(html, "body"); + ul = tree_url_find_xml_element(body, "ul"); + if (ul == NULL) { + xmlFreeDoc(doc); + warn_user("TreeLoadError", + "(<html>...<body>...<ul> not found.)"); + return false; + } + + root = tree_get_root(tree); + tree_url_load_directory(ul, tree, root, callback, callback_data); + tree_set_node_expanded(tree, root, true, false, false); + + xmlFreeDoc(doc); + 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 + */ +static bool tree_url_save_entry(struct node *entry, xmlNode *node) +{ + xmlNode *li, *a; + xmlAttr *href; + const char *text; + + li = xmlNewChild(node, NULL, (const xmlChar *) "li", NULL); + if (li == NULL) + return false; + + + text = tree_url_node_get_title(entry); + if (text == NULL) + return false; + a = xmlNewTextChild(li, NULL, (const xmlChar *) "a", + (const xmlChar *) text); + if (a == NULL) + return false; + + text = tree_url_node_get_url(entry); + if (text == NULL) + return false; + + href = xmlNewProp(a, (const xmlChar *) "href", (const xmlChar *) text); + if (href == NULL) + return false; + 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 + */ +static bool tree_url_save_directory(struct node *directory, xmlNode *node) +{ + struct node *child; + xmlNode *ul, *h4; + const char *text; + + ul = xmlNewChild(node, NULL, (const xmlChar *)"ul", NULL); + if (ul == NULL) + return false; + + for (child = tree_node_get_child(directory); child; + child = tree_node_get_next(child)) { + if (!tree_node_is_folder(child)) { + /* entry */ + if (!tree_url_save_entry(child, ul)) + return false; + } else { + /* directory */ + /* invalid HTML */ + + text = tree_url_node_get_title(child); + if (text == NULL) + return false; + + h4 = xmlNewTextChild(ul, NULL, + (const xmlChar *) "h4", + (const xmlChar *) text); + if (h4 == NULL) + return false; + + if (!tree_url_save_directory(child, ul)) + return false; + } } + + return true; +} + + + + + + + + +/** + * Perform a save to a specified file in the form of a html page + * + * \param filename the file to save to + * \param page_title title of the page + */ +bool tree_urlfile_save(struct tree *tree, const char *filename, + const char *page_title) +{ + int res; + xmlDoc *doc; + xmlNode *html, *head, *title, *body; + + /* Unfortunately the Browse Hotlist format is invalid HTML, + * so this is a lie. + */ + doc = htmlNewDoc( + (const xmlChar *) "http://www.w3.org/TR/html4/strict.dtd", + (const xmlChar *) "-//W3C//DTD HTML 4.01//EN"); + if (doc == NULL) { + warn_user("NoMemory", 0); + return false; + } + + html = xmlNewNode(NULL, (const xmlChar *) "html"); + if (html == NULL) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + xmlDocSetRootElement(doc, html); + + head = xmlNewChild(html, NULL, (const xmlChar *) "head", NULL); + if (head == NULL) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + title = xmlNewTextChild(head, NULL, (const xmlChar *) "title", + (const xmlChar *) page_title); + if (title == NULL) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + body = xmlNewChild(html, NULL, (const xmlChar *) "body", NULL); + if (body == NULL) { + warn_user("NoMemory", 0); + xmlFreeDoc(doc); + return false; + } + + if (!tree_url_save_directory(tree_get_root(tree), 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; +} diff --git a/desktop/tree_url_node.h b/desktop/tree_url_node.h new file mode 100644 index 000000000..4bee73ebc --- /dev/null +++ b/desktop/tree_url_node.h @@ -0,0 +1,56 @@ +/* + * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Creation of URL nodes with use of trees public API + */ + +#ifndef _NETSURF_DESKTOP_TREE_URL_NODE_H_ +#define _NETSURF_DESKTOP_TREE_URL_NODE_H_ + + +#include "desktop/tree.h" + +void tree_url_node_init(void); +struct node *tree_create_URL_node(struct tree *tree, + struct node *parent, const char *url, const char *title, + tree_node_user_callback, void *callback_data); +struct node *tree_create_URL_node_shared(struct tree *tree, + struct node *parent, const char *url, + const struct url_data *data, + tree_node_user_callback, void *callback_data); +void tree_update_URL_node(struct tree *tree,struct node *node, + const char *url, const struct url_data *data, bool shared); +const char *tree_url_node_get_title(struct node *node); +const char *tree_url_node_get_url(struct node *node); +void tree_url_node_edit_title(struct tree *tree, struct node *node); +void tree_url_node_edit_url(struct tree *tree, struct node *node); + +node_callback_resp tree_url_node_callback(void *user_data, + struct node_msg_data *msg_data); + +bool tree_urlfile_load(const char *filename, struct tree *tree, + tree_node_user_callback, void *callback_data); +bool tree_urlfile_save(struct tree *tree, const char *filename, + const char *page_title); + +/* front end specific */ +void tree_icon_name_from_content_type(char *buffer, content_type type); + +#endif diff --git a/framebuffer/misc.c b/framebuffer/misc.c index 889e3ec3f..ac15c5293 100644 --- a/framebuffer/misc.c +++ b/framebuffer/misc.c @@ -35,12 +35,6 @@ void die(const char *error) exit(1); } -bool cookies_update(const char *domain, const struct cookie_data *data) -{ - return true; -} - - /** * Return the filename part of a full path * diff --git a/framebuffer/tree.c b/framebuffer/tree.c index e5cb390b5..61e32e0a5 100644 --- a/framebuffer/tree.c +++ b/framebuffer/tree.c @@ -18,45 +18,56 @@ #include "desktop/tree.h" -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(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) -{ -} +const char tree_directory_icon_name[] = "directory.png"; +const char tree_content_icon_name[] = "content.png"; -void tree_recalculate_node_element(struct node_element *element) -{ -} -void tree_update_URL_node(struct node *node, const char *url, - const struct url_data *data) -{ -} -void tree_resized(struct tree *tree) -{ -} -void tree_set_node_sprite_folder(struct node *node) -{ -} - -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded) +/** + * Translates a content_type to the name of a respective icon + * + * \param content_type content type + * \param buffer buffer for the icon name + */ +void tree_icon_name_from_content_type(char *buffer, content_type type) { + // TODO: design/acquire icons + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_SPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_DRAW + case CONTENT_DRAW: +#endif +#ifdef WITH_ARTWORKS + case CONTENT_ARTWORKS: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + sprintf(buffer, tree_content_icon_name); + break; + } } - diff --git a/gtk/gtk_cookies.c b/gtk/gtk_cookies.c new file mode 100644 index 000000000..1576290ad --- /dev/null +++ b/gtk/gtk_cookies.c @@ -0,0 +1,213 @@ +/* + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Cookies (implementation). + */ + + +#include "desktop/cookies.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "gtk/gtk_gui.h" +#include "gtk/gtk_cookies.h" +#include "gtk/gtk_plotters.h" +#include "gtk/gtk_scaffolding.h" +#include "gtk/gtk_treeview.h" + +#define GLADE_NAME "cookies.glade" + +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; +}; + +static void nsgtk_cookies_init_menu(void); + +/* edit menu */ +MENUPROTO(delete_selected); +MENUPROTO(delete_all); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_domains); +MENUPROTO(expand_cookies); +MENUPROTO(collapse_all); +MENUPROTO(collapse_domains); +MENUPROTO(collapse_cookies); + + +static struct menu_events menu_events[] = { + + /* edit menu */ + MENUEVENT(delete_selected), + MENUEVENT(delete_all), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_domains), + MENUEVENT(expand_cookies), + MENUEVENT(collapse_all), + MENUEVENT(collapse_domains), + MENUEVENT(collapse_cookies), + + {NULL, NULL} +}; + +static struct nsgtk_treeview *cookies_window; +static GladeXML *gladeFile; +GtkWindow *wndCookies; + +/** + * Creates the window for the cookies tree. + */ +void nsgtk_cookies_init(void) +{ + gchar *glade_location = g_strconcat(res_dir_location, GLADE_NAME, NULL); + gladeFile = glade_xml_new(glade_location, NULL, NULL); + g_free(glade_location); + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + + glade_xml_signal_autoconnect(gladeFile); + + wndCookies = GTK_WINDOW(glade_xml_get_widget(gladeFile, "wndCookies")); + window = wndCookies; + + scrolled = GTK_SCROLLED_WINDOW(glade_xml_get_widget(gladeFile, + "cookiesScrolled")); + + drawing_area = GTK_DRAWING_AREA(glade_xml_get_widget(gladeFile, + "cookiesDrawingArea")); + + cookies_window = nsgtk_treeview_create(cookies_get_tree_flags(), window, + scrolled, drawing_area); + + if (cookies_window == NULL) + return; + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(window, "hide", nsgtk_tree_window_hide, cookies_window); + + cookies_initialise(nsgtk_treeview_get_tree(cookies_window)); + + nsgtk_cookies_init_menu(); +} + +/** + * Connects menu events in the cookies window. + */ +void nsgtk_cookies_init_menu() +{ + struct menu_events *event = menu_events; + + while (event->widget != NULL) + { + GtkWidget *w = glade_xml_get_widget(gladeFile, event->widget); + g_signal_connect(G_OBJECT(w), "activate", event->handler, + cookies_window); + event++; + } +} + +/** + * Destroys the cookies window and performs any other necessary cleanup actions. + */ +void nsgtk_cookies_destroy(void) +{ + /* TODO: what about gladeFile? */ + cookies_cleanup(); + nsgtk_treeview_destroy(cookies_window); +} + + +/* edit menu */ +MENUHANDLER(delete_selected) +{ + cookies_delete_selected(); + return TRUE; +} + +MENUHANDLER(delete_all) +{ + cookies_delete_all(); + return TRUE; +} + +MENUHANDLER(select_all) +{ + cookies_select_all(); + return TRUE; +} + +MENUHANDLER(clear_selection) +{ + cookies_clear_selection(); + return TRUE; +} + +/* view menu*/ +MENUHANDLER(expand_all) +{ + cookies_expand_all(); + return TRUE; +} + +MENUHANDLER(expand_domains) +{ + cookies_expand_domains(); + return TRUE; +} + +MENUHANDLER(expand_cookies) +{ + cookies_expand_cookies(); + return TRUE; +} + +MENUHANDLER(collapse_all) +{ + cookies_collapse_all(); + return TRUE; +} + +MENUHANDLER(collapse_domains) +{ + cookies_collapse_domains(); + return TRUE; +} + +MENUHANDLER(collapse_cookies) +{ + cookies_collapse_cookies(); + return TRUE; +} diff --git a/framebuffer/hotlist.c b/gtk/gtk_cookies.h index 727200fe3..7048e2da9 100644 --- a/framebuffer/hotlist.c +++ b/gtk/gtk_cookies.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Vincent Sanders <vince@simtec.co.uk> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,8 +16,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "desktop/browser.h" +/** \file + * Cookies (interface). + */ + +#ifndef __NSGTK_COOKIES_H__ +#define __NSGTK_COOKIES_H__ + +#include <gtk/gtk.h> + +extern GtkWindow *wndCookies; + +void nsgtk_cookies_init(void); +void nsgtk_cookies_destroy(void); -void hotlist_visited(struct hlcache_handle *content) -{ -} +#endif /* __NSGTK_COOKIES_H__ */ diff --git a/gtk/gtk_gui.c b/gtk/gtk_gui.c index 1ca8ce533..a41789a41 100644 --- a/gtk/gtk_gui.c +++ b/gtk/gtk_gui.c @@ -49,20 +49,25 @@ #include "desktop/browser.h" #include "desktop/cookies.h" #include "desktop/gui.h" +#include "desktop/history_global_core.h" #include "desktop/netsurf.h" #include "desktop/options.h" #include "desktop/save_pdf/pdf_plotters.h" #include "desktop/searchweb.h" +#include "desktop/sslcert.h" #include "desktop/textinput.h" -#include "gtk/gtk_gui.h" #include "gtk/dialogs/gtk_options.h" #include "gtk/gtk_completion.h" +#include "gtk/gtk_cookies.h" +#include "gtk/gtk_download.h" +#include "gtk/gtk_filetype.h" +#include "gtk/gtk_gui.h" +#include "gtk/gtk_history.h" +#include "gtk/gtk_hotlist.h" +#include "gtk/gtk_throbber.h" +#include "gtk/gtk_treeview.h" #include "gtk/gtk_window.h" #include "gtk/options.h" -#include "gtk/gtk_throbber.h" -#include "gtk/gtk_history.h" -#include "gtk/gtk_filetype.h" -#include "gtk/gtk_download.h" #include "gtk/gtk_schedule.h" #include "render/box.h" @@ -101,7 +106,9 @@ static struct browser_window *select_menu_bw; static struct form_control *select_menu_control; static void nsgtk_ssl_accept(GtkButton *w, gpointer data); -static void nsgtk_ssl_reject(GtkButton *w, gpointer data); +static void nsgtk_ssl_reject(GtkWidget *w, gpointer data); +static gboolean nsgtk_ssl_delete_event(GtkWidget *w, GdkEvent *event, + gpointer data); static void nsgtk_select_menu_clicked(GtkCheckMenuItem *checkmenuitem, gpointer user_data); #ifdef WITH_PDF_EXPORT @@ -377,6 +384,24 @@ static void check_options(char **respath) LOG(("Using '%s' as download directory", hdir)); option_downloads_directory = hdir; } + + if (!option_tree_icons_dir) { + sfindresourcedef(respath, buf, "icons/", "~/.netsurf/"); + LOG(("Using '%s' as Tree icons dir", buf)); + option_tree_icons_dir = strdup(buf); + } + if (!option_tree_icons_dir) + die("Failed initialising tree icons option"); + + + if (!option_hotlist_path) { + sfindresourcedef(respath, buf, "Hotlist", "~/.netsurf/"); + LOG(("Using '%s' as Hotlist file", buf)); + option_hotlist_path = strdup(buf); + } + if (!option_hotlist_path) + die("Failed initialising hotlist option"); + sfindresourcedef(respath, buf, "Print", "~/.netsurf/"); LOG(("Using '%s' as Print Settings file", buf)); @@ -483,6 +508,10 @@ static void gui_init(int argc, char** argv, char **respath) if (nsgtk_download_init() == false) die("Unable to initialise download window.\n"); + nsgtk_cookies_init(); + nsgtk_hotlist_init(); + sslcert_init(); + if (option_homepage_url != NULL && option_homepage_url[0] != '\0') addr = option_homepage_url; @@ -642,11 +671,16 @@ void gui_quit(void) nsgtk_download_destroy(); urldb_save_cookies(option_cookie_jar); urldb_save(option_url_file); + nsgtk_cookies_destroy(); + nsgtk_history_destroy(); + nsgtk_hotlist_destroy(); + sslcert_cleanup(); free(default_stylesheet_url); free(quirks_stylesheet_url); free(adblock_stylesheet_url); free(option_cookie_file); free(option_cookie_jar); + free(option_tree_icons_dir); free(print_options_file_location); free(search_engines_file_location); free(search_default_ico_location); @@ -744,73 +778,88 @@ void die(const char * const error) } -void hotlist_visited(hlcache_handle *content) -{ -} - void gui_cert_verify(const char *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw) -{ +{ + static struct nsgtk_treeview *ssl_window; + struct sslcert_session_data *data; GladeXML *x = glade_xml_new(glade_ssl_file_location, NULL, NULL); - GtkWindow *wnd = GTK_WINDOW(glade_xml_get_widget(x, "wndSSLProblem")); GtkButton *accept, *reject; - void **session = calloc(sizeof(void *), 5); + void **session = calloc(sizeof(void *), 3); + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; - session[0] = strdup(url); - session[1] = cb; - session[2] = cbpw; - session[3] = x; - session[4] = wnd; + data = sslcert_create_session_data(num, url, cb, cbpw); + + window = GTK_WINDOW( + glade_xml_get_widget(x,"wndSSLProblem")); + scrolled = GTK_SCROLLED_WINDOW( + glade_xml_get_widget(x,"SSLScrolled")); + drawing_area = GTK_DRAWING_AREA( + glade_xml_get_widget(x,"SSLDrawingArea")); - accept = GTK_BUTTON(glade_xml_get_widget(x, "sslaccept")); - reject = GTK_BUTTON(glade_xml_get_widget(x, "sslreject")); - g_signal_connect(G_OBJECT(accept), "clicked", - G_CALLBACK(nsgtk_ssl_accept), (gpointer)session); - g_signal_connect(G_OBJECT(reject), "clicked", - G_CALLBACK(nsgtk_ssl_reject), (gpointer)session); + ssl_window = nsgtk_treeview_create(sslcert_get_tree_flags(), + window, scrolled, drawing_area); + + if (ssl_window == NULL) + return; + + sslcert_load_tree(nsgtk_treeview_get_tree(ssl_window), certs, data); + + accept = GTK_BUTTON(glade_xml_get_widget(x, "sslaccept")); + reject = GTK_BUTTON(glade_xml_get_widget(x, "sslreject")); - gtk_widget_show(GTK_WIDGET(wnd)); + session[0] = x; + session[1] = ssl_window; + session[2] = data; + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(accept, "clicked", nsgtk_ssl_accept, session); + CONNECT(reject, "clicked", nsgtk_ssl_reject, session); + CONNECT(window, "delete_event", G_CALLBACK(nsgtk_ssl_delete_event), + (gpointer)session); + + gtk_widget_show(GTK_WIDGET(window)); } - void nsgtk_ssl_accept(GtkButton *w, gpointer data) { void **session = data; - char *url = session[0]; - nserror (*cb)(bool proceed, void *pw) = session[1]; - void *cbpw = session[2]; - GladeXML *x = session[3]; - GtkWindow *wnd = session[4]; + GladeXML *x = session[0]; + struct nsgtk_treeview *wnd = session[1]; + struct sslcert_session_data *ssl_data = session[2]; - urldb_set_cert_permissions(url, true); - - cb(true, cbpw); - - gtk_widget_destroy(GTK_WIDGET(wnd)); + sslcert_accept(ssl_data); + + nsgtk_treeview_destroy(wnd); g_object_unref(G_OBJECT(x)); - free(url); free(session); } - -void nsgtk_ssl_reject(GtkButton *w, gpointer data) +void nsgtk_ssl_reject(GtkWidget *w, gpointer data) { void **session = data; - nserror (*cb)(bool proceed, void *pw) = session[1]; - void *cbpw = session[2]; - GladeXML *x = session[3]; - GtkWindow *wnd = session[4]; - - cb(false, cbpw); + GladeXML *x = session[0]; + struct nsgtk_treeview *wnd = session[1]; + struct sslcert_session_data *ssl_data = session[2]; - gtk_widget_destroy(GTK_WIDGET(wnd)); + sslcert_reject(ssl_data); + + nsgtk_treeview_destroy(wnd); g_object_unref(G_OBJECT(x)); - free(session[0]); free(session); } +gboolean nsgtk_ssl_delete_event(GtkWidget *w, GdkEvent *event, gpointer data) +{ + nsgtk_ssl_reject(w, data); + return FALSE; +} utf8_convert_ret utf8_to_local_encoding(const char *string, size_t len, char **result) @@ -883,12 +932,6 @@ char *url_to_path(const char *url) return respath; } - -bool cookies_update(const char *domain, const struct cookie_data *data) -{ - return true; -} - #ifdef WITH_PDF_EXPORT void PDF_Password(char **owner_pass, char **user_pass, char *path) diff --git a/gtk/gtk_history.c b/gtk/gtk_history.c index ed0c912f6..75b392fb3 100644 --- a/gtk/gtk_history.c +++ b/gtk/gtk_history.c @@ -1,5 +1,6 @@ /* * Copyright 2006 Rob Kendrick <rjek@rjek.com> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,612 +17,248 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> - -#include <gtk/gtk.h> -#include <glade/glade.h> +#include "desktop/history_global_core.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "gtk/gtk_gui.h" +#include "gtk/gtk_history.h" +#include "gtk/gtk_plotters.h" +#include "gtk/gtk_scaffolding.h" +#include "gtk/gtk_treeview.h" #include "utils/log.h" #include "utils/utils.h" -#include "utils/url.h" -#include "utils/messages.h" -#include "content/urldb.h" -#include "gtk/gtk_history.h" -#include "gtk/gtk_gui.h" -#include "gtk/gtk_window.h" -#include "gtk/gtk_bitmap.h" -enum -{ - SITE_TITLE = 0, - SITE_DOMAIN, - SITE_ADDRESS, - SITE_LASTVISIT, - SITE_TOTALVISITS, - SITE_THUMBNAIL, - SITE_NCOLS +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; }; -enum -{ - DOM_DOMAIN, - DOM_LASTVISIT, - DOM_TOTALVISITS, - DOM_HAS_SITES, - DOM_NCOLS +static void nsgtk_history_init_menu(void); + +/* file menu*/ +MENUPROTO(export); + +/* edit menu */ +MENUPROTO(delete_selected); +MENUPROTO(delete_all); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_directories); +MENUPROTO(expand_addresses); +MENUPROTO(collapse_all); +MENUPROTO(collapse_directories); +MENUPROTO(collapse_addresses); + +MENUPROTO(launch); + +static struct menu_events menu_events[] = { + + /* file menu*/ + MENUEVENT(export), + + /* edit menu */ + MENUEVENT(delete_selected), + MENUEVENT(delete_all), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_directories), + MENUEVENT(expand_addresses), + MENUEVENT(collapse_all), + MENUEVENT(collapse_directories), + MENUEVENT(collapse_addresses), + + MENUEVENT(launch), + {NULL, NULL} }; -GtkWindow *wndHistory; +static struct nsgtk_treeview *global_history_window; static GladeXML *gladeFile; +GtkWindow *wndHistory; -static const gchar* dateToday; -static const gchar* dateYesterday; -static const gchar* dateAt; -static const gchar* domainAll; - -static struct history_model *history; - -static void nsgtk_history_init_model(void); -static void nsgtk_history_init_filters(void); -static void nsgtk_history_init_sort(void); -static void nsgtk_history_init_treeviews(void); -static void nsgtk_history_init_list(void); - -static bool nsgtk_history_add_internal(const char *, const struct url_data *); - -static void nsgtk_history_show_domain(GtkTreeSelection *treesel, - GString *domain_filter); - -static void nsgtk_history_show_all(void); - -static gboolean nsgtk_history_filter_search(GtkTreeModel *model, - GtkTreeIter *iter, GtkWidget *search_entry); -static gboolean nsgtk_history_filter_sites(GtkTreeModel *model, - GtkTreeIter *iter, GString *domain_filter); - -static gchar *nsgtk_history_parent_get(gchar *domain); -static void nsgtk_history_parent_update(gchar *path, const struct url_data *data); - -static void nsgtk_history_domain_sort_changed(GtkComboBox *combo); -static gint nsgtk_history_domain_sort_compare(GtkTreeModel *model, GtkTreeIter *a, - GtkTreeIter *b, gint sort_column); -static void nsgtk_history_domain_set_visible (GtkTreeModel *model, - GtkTreePath *path, GtkTreeIter *iter, gboolean has_sites); - -static void nsgtk_history_search(void); -static void nsgtk_history_search_clear (GtkEntry *entry); - -static gchar *nsgtk_history_date_parse(time_t visit_time); -static void nsgtk_history_row_activated(GtkTreeView *, GtkTreePath *, - GtkTreeViewColumn *); -static void nsgtk_history_update_info(GtkTreeSelection *treesel, - gboolean domain); -static void nsgtk_history_scroll_top (GtkScrolledWindow *scrolled_window); +/** + * Creates the window for the global history tree. + */ bool nsgtk_history_init(void) { - dateToday = messages_get("DateToday"); - dateYesterday = messages_get("DateYesterday"); - dateAt = messages_get("DateAt"); - domainAll = messages_get("DomainAll"); - + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + gladeFile = glade_xml_new(glade_history_file_location, NULL, NULL); if (gladeFile == NULL) return false; glade_xml_signal_autoconnect(gladeFile); - wndHistory = GTK_WINDOW(glade_xml_get_widget(gladeFile, "wndHistory")); - - nsgtk_history_init_model(); - nsgtk_history_init_list(); - nsgtk_history_init_filters(); - nsgtk_history_init_sort(); - nsgtk_history_init_treeviews(); - - nsgtk_history_show_all(); - - return true; -} - -void nsgtk_history_init_model(void) -{ - history = malloc(sizeof(struct history_model)); - history->history_list = gtk_list_store_new(SITE_NCOLS, - G_TYPE_STRING, /* title */ - G_TYPE_STRING, /* domain */ - G_TYPE_STRING, /* address */ - G_TYPE_INT, /* last visit */ - G_TYPE_INT, /* num visits */ - G_TYPE_POINTER); /* thumbnail */ - history->history_filter = gtk_tree_model_filter_new( - GTK_TREE_MODEL(history->history_list), NULL); + wndHistory = GTK_WINDOW(glade_xml_get_widget(gladeFile, "wndHistory")); - history->site_filter = gtk_tree_model_filter_new( - history->history_filter,NULL); - history->site_sort = gtk_tree_model_sort_new_with_model(history->site_filter); - history->site_treeview = GTK_TREE_VIEW(glade_xml_get_widget(gladeFile, - "treeHistory")); - history->site_selection = - gtk_tree_view_get_selection(history->site_treeview); + window = wndHistory; - history->domain_list = gtk_list_store_new(DOM_NCOLS, - G_TYPE_STRING, /* domain */ - G_TYPE_INT, /* last visit */ - G_TYPE_INT, /* num visits */ - G_TYPE_BOOLEAN); /* has sites */ - history->domain_filter = gtk_tree_model_filter_new( - GTK_TREE_MODEL(history->domain_list), NULL); - history->domain_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - history->domain_sort = gtk_tree_model_sort_new_with_model( - history->domain_filter); - history->domain_treeview = GTK_TREE_VIEW(glade_xml_get_widget( - gladeFile,"treeDomain")); - history->domain_selection = - gtk_tree_view_get_selection(history->domain_treeview); -} + scrolled = GTK_SCROLLED_WINDOW(glade_xml_get_widget(gladeFile, + "globalHistoryScrolled")); -void nsgtk_history_init_list(void) -{ - GtkTreeIter iter; + drawing_area = GTK_DRAWING_AREA(glade_xml_get_widget(gladeFile, + "globalHistoryDrawingArea")); + + global_history_window = nsgtk_treeview_create( + history_global_get_tree_flags(), window, scrolled, + drawing_area); - gtk_list_store_clear(history->history_list); - gtk_list_store_clear(history->domain_list); + if (global_history_window == NULL) + return false; - gtk_list_store_append(history->domain_list, &iter); - gtk_list_store_set(history->domain_list, &iter, - DOM_DOMAIN, domainAll, - DOM_LASTVISIT, -2, - DOM_TOTALVISITS, -2, - DOM_HAS_SITES, TRUE, - -1); +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) - urldb_iterate_entries(nsgtk_history_add_internal); -} - -void nsgtk_history_init_filters(void) -{ - GtkWidget *search_entry, *clear_button; - GString *filter_string = g_string_new(NULL); - - search_entry = glade_xml_get_widget(gladeFile,"entrySearch"); - clear_button = glade_xml_get_widget(gladeFile,"buttonClearSearch"); + CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(window, "hide", nsgtk_tree_window_hide, global_history_window); - g_signal_connect(G_OBJECT(search_entry), "changed", - G_CALLBACK(nsgtk_history_search), NULL); - g_signal_connect_swapped(G_OBJECT(clear_button), "clicked", - G_CALLBACK(nsgtk_history_search_clear), - GTK_ENTRY(search_entry)); + history_global_initialise( + nsgtk_treeview_get_tree(global_history_window)); - gtk_tree_model_filter_set_visible_func( - GTK_TREE_MODEL_FILTER(history->history_filter), - (GtkTreeModelFilterVisibleFunc) - nsgtk_history_filter_search, search_entry, NULL); - gtk_tree_model_filter_set_visible_func( - GTK_TREE_MODEL_FILTER(history->site_filter), - (GtkTreeModelFilterVisibleFunc) - nsgtk_history_filter_sites, filter_string, NULL); - gtk_tree_model_filter_set_visible_column( - GTK_TREE_MODEL_FILTER(history->domain_filter), - DOM_HAS_SITES); - - g_signal_connect(G_OBJECT(history->site_selection), "changed", - G_CALLBACK(nsgtk_history_update_info), FALSE); - g_signal_connect(G_OBJECT(history->domain_selection), "changed", - G_CALLBACK(nsgtk_history_show_domain), filter_string); + nsgtk_history_init_menu(); + + return true; } -void nsgtk_history_init_sort(void) -{ - GtkWidget *domain_window = glade_xml_get_widget(gladeFile, - "windowDomain"); - GtkComboBox *sort_combo_box = - GTK_COMBO_BOX(glade_xml_get_widget( - gladeFile, "comboSort")); - gtk_combo_box_set_active(sort_combo_box, 0); - - g_signal_connect(G_OBJECT(sort_combo_box), "changed", - G_CALLBACK(nsgtk_history_domain_sort_changed), NULL); - g_signal_connect_swapped(G_OBJECT(sort_combo_box), "changed", - G_CALLBACK(nsgtk_history_scroll_top), domain_window); - - gtk_tree_sortable_set_sort_func( - GTK_TREE_SORTABLE(history->domain_sort), - DOM_LASTVISIT, (GtkTreeIterCompareFunc) - nsgtk_history_domain_sort_compare, - GUINT_TO_POINTER(DOM_LASTVISIT), NULL); - gtk_tree_sortable_set_sort_func( - GTK_TREE_SORTABLE(history->domain_sort), - DOM_TOTALVISITS, (GtkTreeIterCompareFunc) - nsgtk_history_domain_sort_compare, - GUINT_TO_POINTER(DOM_TOTALVISITS), NULL); - gtk_tree_sortable_set_sort_func( - GTK_TREE_SORTABLE(history->site_sort), - SITE_LASTVISIT, (GtkTreeIterCompareFunc) - nsgtk_history_domain_sort_compare, - GUINT_TO_POINTER(SITE_LASTVISIT), NULL); - gtk_tree_sortable_set_sort_func( - GTK_TREE_SORTABLE(history->site_sort), - SITE_TOTALVISITS, (GtkTreeIterCompareFunc) - nsgtk_history_domain_sort_compare, - GUINT_TO_POINTER(SITE_TOTALVISITS), NULL); -} - -void nsgtk_history_init_treeviews(void) -{ - GtkCellRenderer *renderer; - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(history->site_treeview, -1, - messages_get("Title"), renderer, - "text", SITE_TITLE, - NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(history->domain_treeview, - -1, messages_get("Domain"), renderer, - "markup", DOM_DOMAIN, - NULL); - - gtk_tree_view_set_model(history->site_treeview, history->site_sort); - gtk_tree_view_set_model(history->domain_treeview, history->domain_sort); - - g_signal_connect(history->site_treeview, "row-activated", - G_CALLBACK(nsgtk_history_row_activated), NULL); -} -bool nsgtk_history_add_internal(const char *url, const struct url_data *data) +/** + * Connects menu events in the global history window. + */ +void nsgtk_history_init_menu(void) { - GtkTreeIter iter; - gchar *domain, *path; - if (url_host(url, &domain) != URL_FUNC_OK) - strcpy(domain, messages_get("gtkUnknownHost")); + struct menu_events *event = menu_events; - if (data->visits > 0) + while (event->widget != NULL) { - path = nsgtk_history_parent_get(domain); - nsgtk_history_parent_update(path, data); - - gtk_list_store_append(history->history_list, &iter); - gtk_list_store_set(history->history_list, &iter, - SITE_TITLE, data->title ? data->title : - url, - SITE_DOMAIN, domain, - SITE_ADDRESS, url, - SITE_LASTVISIT, data->last_visit, - SITE_TOTALVISITS, data->visits, - SITE_THUMBNAIL, - gtk_bitmap_get_primary( - urldb_get_thumbnail(url)), - -1); + GtkWidget *w = glade_xml_get_widget(gladeFile, event->widget); + g_signal_connect(G_OBJECT(w), "activate", event->handler, + global_history_window); + event++; } - return true; } -gchar *nsgtk_history_parent_get(gchar *domain) -{ - GtkTreeIter iter; - gchar *path; - - /* Adds an extra entry in the list to act as the root domain - * (which will keep track of things like visits to all sites - * in the domain), This does not work as a tree because the - * children cannot be displayed if the root is hidden - * (which would conflict with the site view) */ - path = g_hash_table_lookup(history->domain_hash, domain); - - if (path == NULL){ - gtk_list_store_append(history->domain_list, &iter); - gtk_list_store_set(history->domain_list, &iter, - DOM_DOMAIN, domain, - DOM_LASTVISIT, messages_get("gtkUnknownHost"), - DOM_TOTALVISITS, 0, - -1); - - path = gtk_tree_model_get_string_from_iter( - GTK_TREE_MODEL(history->domain_list), &iter); - g_hash_table_insert(history->domain_hash, domain, - path); - } - return path; +/** + * Destroys the global history window and performs any other necessary cleanup + * actions. + */ +void nsgtk_history_destroy(void) +{ + /* TODO: what about gladeFile? */ + history_global_cleanup(); + nsgtk_treeview_destroy(global_history_window); } -void nsgtk_history_parent_update(gchar *path, const struct url_data *data) -{ - GtkTreeIter iter; - gint num_visits, last_visit; - - gtk_tree_model_get_iter_from_string( - GTK_TREE_MODEL(history->domain_list), &iter, path); - gtk_tree_model_get(GTK_TREE_MODEL(history->domain_list), &iter, - DOM_TOTALVISITS, &num_visits, - DOM_LASTVISIT, &last_visit, - -1); - - gtk_list_store_set(history->domain_list, &iter, - DOM_TOTALVISITS, num_visits + data->visits, - DOM_LASTVISIT, max(last_visit,data->last_visit), - -1); - - /* Handle "All" */ - gtk_tree_model_get_iter_from_string( - GTK_TREE_MODEL(history->domain_list), &iter, "0"); - gtk_tree_model_get(GTK_TREE_MODEL(history->domain_list), &iter, - DOM_TOTALVISITS, &num_visits, - DOM_LASTVISIT, &last_visit, - -1); - - gtk_list_store_set(history->domain_list, &iter, - DOM_TOTALVISITS, num_visits + data->visits, - DOM_LASTVISIT, max(last_visit,data->last_visit), - -1); -} -void nsgtk_history_show_domain(GtkTreeSelection *treesel, - GString *domain_filter) +/* file menu*/ +MENUHANDLER(export) { - GtkTreeIter iter; - GtkTreeModel *model; - - if (gtk_tree_selection_get_selected(treesel, &model, &iter)) { - gtk_tree_model_get(model, &iter, DOM_DOMAIN, - &domain_filter->str, -1); - gtk_tree_model_filter_refilter( - GTK_TREE_MODEL_FILTER(history->site_filter)); + GtkWidget *save_dialog; + save_dialog = gtk_file_chooser_dialog_new("Save File", + wndHistory, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog), + getenv("HOME") ? getenv("HOME") : "/"); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), + "history.html"); + + if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(save_dialog)); + + history_global_export(filename); + g_free(filename); } - nsgtk_history_update_info(treesel, TRUE); -} + gtk_widget_destroy(save_dialog); -static void nsgtk_history_show_all(void) -{ - GtkTreePath *path = gtk_tree_path_new_from_string("0"); - - gtk_tree_selection_select_path(history->domain_selection, path); - - gtk_tree_path_free(path); + return TRUE; } -gboolean nsgtk_history_filter_search(GtkTreeModel *model, GtkTreeIter *iter, - GtkWidget *search_entry) +/* edit menu */ +MENUHANDLER(delete_selected) { - gchar *title, *address, *domain, *path; - gint result; - GtkTreeIter new_iter; - const gchar *search = gtk_entry_get_text(GTK_ENTRY(search_entry)); - - gtk_tree_model_get(model, iter, SITE_TITLE, &title, - SITE_ADDRESS, &address, - SITE_DOMAIN, &domain, - -1); - - if (title) - result = (strstr(title, search) || strstr(address, search)); - else - result = FALSE; - - if (result) { - path = g_hash_table_lookup(history->domain_hash, domain); - gtk_tree_model_get_iter_from_string( - GTK_TREE_MODEL(history->domain_list),&new_iter, - path); - - nsgtk_history_domain_set_visible( - GTK_TREE_MODEL(history->domain_list), NULL, - &new_iter, result); - } - - g_free(title); - g_free(address); - g_free(domain); - - return result; + history_global_delete_selected(); + return TRUE; } -gboolean nsgtk_history_filter_sites(GtkTreeModel *model, GtkTreeIter *iter, - GString *domain_filter) +MENUHANDLER(delete_all) { - gchar *domain; - gboolean domain_match; - - gtk_tree_model_get(model, iter, SITE_DOMAIN, &domain, -1); - - if (domain && domain_filter->str) - domain_match = g_str_equal(domain, domain_filter->str) || - g_str_equal(domain_filter->str, - domainAll); - else - domain_match = FALSE; - - g_free(domain); - return domain_match; + history_global_delete_all(); + return TRUE; } -void nsgtk_history_domain_sort_changed(GtkComboBox *combo) +MENUHANDLER(select_all) { - gint domain_options[] = { DOM_DOMAIN, DOM_LASTVISIT, DOM_TOTALVISITS }; - gint site_options[] = { SITE_TITLE, SITE_LASTVISIT, SITE_TOTALVISITS }; - gint sort = gtk_combo_box_get_active(combo); - - gtk_tree_sortable_set_sort_column_id( - GTK_TREE_SORTABLE(history->domain_sort), - domain_options[sort], GTK_SORT_ASCENDING); - gtk_tree_sortable_set_sort_column_id( - GTK_TREE_SORTABLE(history->site_sort), - site_options[sort], GTK_SORT_ASCENDING); + history_global_select_all(); + return TRUE; } -gint nsgtk_history_domain_sort_compare(GtkTreeModel *model, GtkTreeIter *a, - GtkTreeIter *b, gint sort_column) +MENUHANDLER(clear_selection) { - gint comparable_a; - gint comparable_b; - - gtk_tree_model_get(model, a, sort_column, &comparable_a, -1); - gtk_tree_model_get(model, b, sort_column, &comparable_b, -1); - - /* Make sure "All" stays at the top */ - if (comparable_a < 0 || comparable_b < 0) - return comparable_a - comparable_b; - else - return comparable_b - comparable_a; + history_global_clear_selection(); + return TRUE; } -void nsgtk_history_domain_set_visible (GtkTreeModel *model, GtkTreePath *path, - GtkTreeIter *iter, gboolean has_sites) +/* view menu*/ +MENUHANDLER(expand_all) { - gchar *string = gtk_tree_model_get_string_from_iter(model, iter); - - if (!g_str_equal(string, "0")) /* "All" */ - gtk_list_store_set(GTK_LIST_STORE(model), iter, - DOM_HAS_SITES, has_sites, -1); - - g_free(string); + history_global_expand_all(); + return TRUE; } -void nsgtk_history_search() +MENUHANDLER(expand_directories) { - gtk_tree_model_foreach(GTK_TREE_MODEL(history->domain_list), - (GtkTreeModelForeachFunc) - nsgtk_history_domain_set_visible, FALSE); - - nsgtk_history_show_all(); - gtk_tree_model_filter_refilter( - GTK_TREE_MODEL_FILTER(history->history_filter)); + history_global_expand_directories(); + return TRUE; } -void nsgtk_history_search_clear (GtkEntry *entry) +MENUHANDLER(expand_addresses) { - gtk_entry_set_text(entry, ""); -} - -gchar *nsgtk_history_date_parse(time_t visit_time) -{ - char *date_string = malloc(30); - char format[30]; - time_t current_time = time(NULL); - int current_day = localtime(¤t_time)->tm_yday; - struct tm *visit_date = localtime(&visit_time); - - if (visit_date->tm_yday == current_day) - snprintf(format, 30, "%s %s %%I:%%M %%p", - dateToday, dateAt); - else if (current_day - visit_date->tm_yday == 1) - snprintf(format, 30, "%s %s %%I:%%M %%p", - dateYesterday, dateAt); - else if (current_day - visit_date->tm_yday < 7) - snprintf(format, 30, "%%A %s %%I:%%M %%p", - dateAt); - else - snprintf(format, 30, "%%B %%d, %%Y"); - - strftime(date_string, 30, format, visit_date); - - return date_string; + history_global_expand_addresses(); + return TRUE; } - - -void nsgtk_history_row_activated(GtkTreeView *tv, GtkTreePath *path, - GtkTreeViewColumn *column) -{ - GtkTreeModel *model; - GtkTreeIter iter; - - model = gtk_tree_view_get_model(tv); - if (gtk_tree_model_get_iter(model, &iter, path)) - { - gchar *address; - gtk_tree_model_get(model, &iter, SITE_ADDRESS, &address, -1); - - browser_window_create(address, NULL, NULL, true, false); - } +MENUHANDLER(collapse_all) +{ + history_global_collapse_all(); + return TRUE; } -void nsgtk_history_update_info(GtkTreeSelection *treesel, gboolean domain) +MENUHANDLER(collapse_directories) { - GtkTreeIter iter; - GtkTreeModel *model; - gboolean has_selection; - - has_selection = gtk_tree_selection_get_selected(treesel, &model, &iter); - - if (has_selection && domain) { - gchar *b; - gint i; - char buf[20]; - gboolean all = g_str_equal(gtk_tree_model_get_string_from_iter( - model, &iter), "0"); - - /* Address */ - gtk_tree_model_get(model, &iter, DOM_DOMAIN, &b, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryAddress")), - all ? "-" : b); - g_free(b); - /* Last Visit */ - gtk_tree_model_get(model, &iter, DOM_LASTVISIT, &i, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryLastVisit")), - nsgtk_history_date_parse(i)); - - /* Total Visits */ - gtk_tree_model_get(model, &iter, DOM_TOTALVISITS, &i, -1); - snprintf(buf, 20, "%d", i); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryVisits")), - buf); - } else if (has_selection){ - GdkPixbuf *thumb; - gchar *b; - gint i; - char buf[20]; - /* Address */ - gtk_tree_model_get(model, &iter, SITE_ADDRESS, &b, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryAddress")), b); - g_free(b); - /* Last Visit */ - gtk_tree_model_get(model, &iter, SITE_LASTVISIT, &i, -1); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryLastVisit")), - nsgtk_history_date_parse(i)); - - /* Total Visits */ - gtk_tree_model_get(model, &iter, SITE_TOTALVISITS, &i, -1); - snprintf(buf, 20, "%d", i); - gtk_label_set_text(GTK_LABEL(glade_xml_get_widget(gladeFile, - "labelHistoryVisits")), buf); - - gtk_tree_model_get(model, &iter, SITE_THUMBNAIL, &thumb, -1); - gtk_image_set_from_pixbuf(GTK_IMAGE( - glade_xml_get_widget(gladeFile, - "imageThumbnail")), thumb); - g_object_set(G_OBJECT(glade_xml_get_widget( - gladeFile, "imageFrame")), - "visible", (bool)thumb, NULL); - } + history_global_collapse_directories(); + return TRUE; } -void nsgtk_history_scroll_top (GtkScrolledWindow *scrolled_window) +MENUHANDLER(collapse_addresses) { - GtkAdjustment *adjustment = - gtk_scrolled_window_get_vadjustment(scrolled_window); - - gtk_adjustment_set_value(adjustment, 0); - - gtk_scrolled_window_set_vadjustment(scrolled_window, adjustment); + history_global_collapse_addresses(); + return TRUE; } - -void global_history_add(const char *url) -{ - const struct url_data *data; - data = urldb_get_url_data(url); - if (!data) - return; - - nsgtk_history_add_internal(url, data); +MENUHANDLER(launch) +{ + history_global_launch_selected(); + return TRUE; } diff --git a/gtk/gtk_history.h b/gtk/gtk_history.h index 02cb7f920..93d57b2b7 100644 --- a/gtk/gtk_history.h +++ b/gtk/gtk_history.h @@ -1,5 +1,6 @@ /* * Copyright 2006 Rob Kendrick <rjek@rjek.com> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -23,22 +24,7 @@ extern GtkWindow *wndHistory; - -struct history_model { - GtkListStore *history_list; - GtkTreeModel *history_filter; - GtkTreeModel *site_filter; - GtkTreeModel *site_sort; - GtkTreeView *site_treeview; - GtkTreeSelection *site_selection; - GtkListStore *domain_list; - GtkTreeModel *domain_filter; - GHashTable *domain_hash; - GtkTreeModel *domain_sort; - GtkTreeView *domain_treeview; - GtkTreeSelection *domain_selection; -}; - bool nsgtk_history_init(void); +void nsgtk_history_destroy(void); #endif /* __NSGTK_HISTORY_H__ */ diff --git a/gtk/gtk_hotlist.c b/gtk/gtk_hotlist.c new file mode 100644 index 000000000..905050260 --- /dev/null +++ b/gtk/gtk_hotlist.c @@ -0,0 +1,277 @@ +/* + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "desktop/hotlist.h" +#include "desktop/options.h" +#include "desktop/plotters.h" +#include "desktop/tree.h" +#include "gtk/gtk_gui.h" +#include "gtk/gtk_hotlist.h" +#include "gtk/options.h" +#include "gtk/gtk_plotters.h" +#include "gtk/gtk_scaffolding.h" +#include "gtk/gtk_treeview.h" +#include "utils/log.h" + +#define GLADE_NAME "hotlist.glade" + +#define MENUPROTO(x) static gboolean nsgtk_on_##x##_activate( \ + GtkMenuItem *widget, gpointer g) +#define MENUEVENT(x) { #x, G_CALLBACK(nsgtk_on_##x##_activate) } +#define MENUHANDLER(x) gboolean nsgtk_on_##x##_activate(GtkMenuItem *widget, \ + gpointer g) + +struct menu_events { + const char *widget; + GCallback handler; +}; + +static void nsgtk_hotlist_init_menu(void); + +/* file menu*/ +MENUPROTO(export); +MENUPROTO(new_folder); +MENUPROTO(new_entry); + +/* edit menu */ +MENUPROTO(edit_selected); +MENUPROTO(delete_selected); +MENUPROTO(select_all); +MENUPROTO(clear_selection); + +/* view menu*/ +MENUPROTO(expand_all); +MENUPROTO(expand_directories); +MENUPROTO(expand_addresses); +MENUPROTO(collapse_all); +MENUPROTO(collapse_directories); +MENUPROTO(collapse_addresses); + +MENUPROTO(launch); + +static struct menu_events menu_events[] = { + + /* file menu*/ + MENUEVENT(export), + MENUEVENT(new_folder), + MENUEVENT(new_entry), + + /* edit menu */ + MENUEVENT(edit_selected), + MENUEVENT(delete_selected), + MENUEVENT(select_all), + MENUEVENT(clear_selection), + + /* view menu*/ + MENUEVENT(expand_all), + MENUEVENT(expand_directories), + MENUEVENT(expand_addresses), + MENUEVENT(collapse_all), + MENUEVENT(collapse_directories), + MENUEVENT(collapse_addresses), + + MENUEVENT(launch), + {NULL, NULL} +}; + +static struct nsgtk_treeview *hotlist_window; +static GladeXML *gladeFile; +GtkWindow *wndHotlist; + + +/** + * Creates the window for the hotlist tree. + */ +void nsgtk_hotlist_init() +{ + gchar *glade_location = g_strconcat(res_dir_location, GLADE_NAME, NULL); + gladeFile = glade_xml_new(glade_location, NULL, NULL); + g_free(glade_location); + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + + glade_xml_signal_autoconnect(gladeFile); + + wndHotlist = GTK_WINDOW(glade_xml_get_widget(gladeFile, "wndHotlist")); + window = wndHotlist; + + scrolled = GTK_SCROLLED_WINDOW(glade_xml_get_widget(gladeFile, + "hotlistScrolled")); + + drawing_area = GTK_DRAWING_AREA(glade_xml_get_widget(gladeFile, + "hotlistDrawingArea")); + + + hotlist_window = nsgtk_treeview_create(hotlist_get_tree_flags(), window, + scrolled, drawing_area); + + if (hotlist_window == NULL) + return; + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(window, "delete_event", gtk_widget_hide_on_delete, NULL); + CONNECT(window, "hide", nsgtk_tree_window_hide, hotlist_window); + + hotlist_initialise(nsgtk_treeview_get_tree(hotlist_window), + option_hotlist_path); + + nsgtk_hotlist_init_menu(); +} + + +/** + * Connects menu events in the hotlist window. + */ +void nsgtk_hotlist_init_menu(void) +{ + struct menu_events *event = menu_events; + + while (event->widget != NULL) + { + GtkWidget *w = glade_xml_get_widget(gladeFile, event->widget); + g_signal_connect(G_OBJECT(w), "activate", event->handler, + hotlist_window); + event++; + } +} + + +/** + * Destroys the hotlist window and performs any other necessary cleanup actions. + */ +void nsgtk_hotlist_destroy(void) +{ + /* TODO: what about gladeFile? */ + hotlist_cleanup(option_hotlist_path); + nsgtk_treeview_destroy(hotlist_window); +} + + +/* file menu*/ +MENUHANDLER(export) +{ + GtkWidget *save_dialog; + save_dialog = gtk_file_chooser_dialog_new("Save File", + wndHotlist, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(save_dialog), + getenv("HOME") ? getenv("HOME") : "/"); + + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog), + "hotlist.html"); + + if (gtk_dialog_run(GTK_DIALOG(save_dialog)) == GTK_RESPONSE_ACCEPT) { + gchar *filename = gtk_file_chooser_get_filename( + GTK_FILE_CHOOSER(save_dialog)); + + hotlist_export(filename); + g_free(filename); + } + + gtk_widget_destroy(save_dialog); + + return TRUE; +} + +MENUHANDLER(new_folder) +{ + hotlist_add_folder(); + return TRUE; +} + +MENUHANDLER(new_entry) +{ + hotlist_add_entry(); + return TRUE; +} + +/* edit menu */ +MENUHANDLER(edit_selected) +{ + hotlist_edit_selected(); + return TRUE; +} + +MENUHANDLER(delete_selected) +{ + hotlist_delete_selected(); + return TRUE; +} + +MENUHANDLER(select_all) +{ + hotlist_select_all(); + return TRUE; +} + +MENUHANDLER(clear_selection) +{ + hotlist_clear_selection(); + return TRUE; +} + +/* view menu*/ +MENUHANDLER(expand_all) +{ + hotlist_expand_all(); + return TRUE; +} + +MENUHANDLER(expand_directories) +{ + hotlist_expand_directories(); + return TRUE; +} + +MENUHANDLER(expand_addresses) +{ + hotlist_expand_addresses(); + return TRUE; +} + +MENUHANDLER(collapse_all) +{ + hotlist_collapse_all(); + return TRUE; +} + +MENUHANDLER(collapse_directories) +{ + hotlist_collapse_directories(); + return TRUE; +} + +MENUHANDLER(collapse_addresses) +{ + hotlist_collapse_addresses(); + return TRUE; +} + +MENUHANDLER(launch) +{ + hotlist_launch_selected(); + return TRUE; +} diff --git a/framebuffer/history.c b/gtk/gtk_hotlist.h index b0cf20495..22dc0a45f 100644 --- a/framebuffer/history.c +++ b/gtk/gtk_hotlist.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Vincent Sanders <vince@simtec.co.uk> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,26 +16,18 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "content/urldb.h" -#include "desktop/browser.h" - -void global_history_add(const char *url) -{ - const struct url_data *data; - - data = urldb_get_url_data(url); - if (!data) - return; +/** \file + * GTK hotlist (interface). + */ +#ifndef __NSGTK_HOTLIST_H__ +#define __NSGTK_HOTLIST_H__ -} +#include <gtk/gtk.h> -void global_history_add_recent(const char *url) -{ -} +extern GtkWindow *wndHotlist; -char **global_history_get_recent(int *count) -{ - return NULL; -} +void nsgtk_hotlist_init(void); +void nsgtk_hotlist_destroy(void); +#endif /* __NSGTK_HOTLIST_H__ */ diff --git a/gtk/gtk_menu.c b/gtk/gtk_menu.c index af9d0b67c..6fb5fc313 100644 --- a/gtk/gtk_menu.c +++ b/gtk/gtk_menu.c @@ -397,6 +397,7 @@ static struct nsgtk_nav_menu *nsgtk_menu_nav_menu(GtkAccelGroup *group, free(ret); return NULL; } + IMAGE_ITEM(nav, back, gtkBack, ret, group); IMAGE_ITEM(nav, forward, gtkForward, ret, group); IMAGE_ITEM(nav, home, gtkHome, ret, group); @@ -407,6 +408,8 @@ static struct nsgtk_nav_menu *nsgtk_menu_nav_menu(GtkAccelGroup *group, IMAGE_ITEM(nav, addbookmarks, gtkAddBookMarks, ret, group); IMAGE_ITEM(nav, showbookmarks, gtkShowBookMarks, ret, group); ADD_SEP(nav, ret); + IMAGE_ITEM(nav, showcookies, gtkShowCookies, ret, group); + ADD_SEP(nav, ret); IMAGE_ITEM(nav, openlocation, gtkOpenLocation, ret, group); ATTACH_PARENT(parent, gtkNavigate, ret->nav, group); diff --git a/gtk/gtk_menu.h b/gtk/gtk_menu.h index 2ddacd891..434eafd1b 100644 --- a/gtk/gtk_menu.h +++ b/gtk/gtk_menu.h @@ -78,6 +78,7 @@ struct nsgtk_nav_menu { GtkImageMenuItem *globalhistory_menuitem; GtkImageMenuItem *addbookmarks_menuitem; GtkImageMenuItem *showbookmarks_menuitem; + GtkImageMenuItem *showcookies_menuitem; GtkImageMenuItem *openlocation_menuitem; }; diff --git a/gtk/gtk_plotters.c b/gtk/gtk_plotters.c index 9598e2022..1428710ec 100644 --- a/gtk/gtk_plotters.c +++ b/gtk/gtk_plotters.c @@ -89,9 +89,9 @@ void nsgtk_set_colour(colour c) g / 255.0, b / 255.0, 1.0); } -/** Plot a caret. +/** Plot a caret. * - * @note It is assumed that the plotters have been set up. + * @note It is assumed that the plotters have been set up. */ void nsgtk_plot_caret(int x, int y, int h) { @@ -234,8 +234,12 @@ static bool nsgtk_plot_line(int x0, int y0, int x1, int y1, const plot_style_t * else cairo_set_line_width(current_cr, style->stroke_width); - cairo_move_to(current_cr, x0 + 0.5, y0 + 0.5); - cairo_line_to(current_cr, x1 + 0.5, y1 + 0.5); + /* core expects horizontal and vertical lines to be on pixels, not + * between pixels */ + cairo_move_to(current_cr, (x0 == x1) ? x0 + 0.5 : x0, + (y0 == y1) ? y0 + 0.5 : y0); + cairo_line_to(current_cr, (x0 == x1) ? x1 + 0.5 : x1, + (y0 == y1) ? y1 + 0.5 : y1); cairo_stroke(current_cr); return true; @@ -304,7 +308,7 @@ static bool nsgtk_plot_polygon(const int *p, unsigned int n, const plot_style_t -static bool nsgtk_plot_text(int x, int y, const char *text, size_t length, +static bool nsgtk_plot_text(int x, int y, const char *text, size_t length, const plot_font_style_t *fstyle) { return nsfont_paint(x, y, text, length, fstyle); diff --git a/gtk/gtk_scaffolding.c b/gtk/gtk_scaffolding.c index b2c7c7dba..e970a60af 100644 --- a/gtk/gtk_scaffolding.c +++ b/gtk/gtk_scaffolding.c @@ -34,6 +34,7 @@ #include "css/utils.h" #include "desktop/browser.h" #include "desktop/history_core.h" +#include "desktop/hotlist.h" #include "desktop/gui.h" #include "desktop/netsurf.h" #include "desktop/options.h" @@ -49,6 +50,8 @@ #include "desktop/searchweb.h" #include "desktop/selection.h" #include "desktop/textinput.h" +#include "desktop/tree.h" +#include "gtk/gtk_cookies.h" #include "gtk/gtk_completion.h" #include "gtk/dialogs/gtk_options.h" #include "gtk/dialogs/gtk_about.h" @@ -57,6 +60,7 @@ #include "gtk/gtk_download.h" #include "gtk/gtk_gui.h" #include "gtk/gtk_history.h" +#include "gtk/gtk_hotlist.h" #include "gtk/gtk_menu.h" #include "gtk/gtk_plotters.h" #include "gtk/gtk_print.h" @@ -66,6 +70,7 @@ #include "gtk/gtk_theme.h" #include "gtk/gtk_throbber.h" #include "gtk/gtk_toolbar.h" +#include "gtk/gtk_treeview.h" #include "gtk/gtk_window.h" #include "gtk/options.h" #include "gtk/sexy_icon_entry.h" @@ -217,7 +222,7 @@ void nsgtk_window_close(struct gtk_scaffolding *g) if ((g->history_window) && (g->history_window->window)) { gtk_widget_destroy(GTK_WIDGET(g->history_window->window)); } - + if (--open_windows == 0) netsurf_quit = true; @@ -1263,17 +1268,34 @@ MULTIHANDLER(globalhistory) { gtk_widget_show(GTK_WIDGET(wndHistory)); gdk_window_raise(GTK_WIDGET(wndHistory)->window); - + return TRUE; } MULTIHANDLER(addbookmarks) { + struct browser_window *bw = gui_window_get_browser_window(g->top_level); + + if (bw == NULL || bw->current_content == NULL || + content_get_url(bw->current_content) == NULL) + return TRUE; + hotlist_add_page(content_get_url(bw->current_content)); return TRUE; } MULTIHANDLER(showbookmarks) { + gtk_widget_show(GTK_WIDGET(wndHotlist)); + gdk_window_raise(GTK_WIDGET(wndHotlist)->window); + + return TRUE; +} + +MULTIHANDLER(showcookies) +{ + gtk_widget_show(GTK_WIDGET(wndCookies)); + gdk_window_raise(GTK_WIDGET(wndCookies)->window); + return TRUE; } @@ -1394,6 +1416,7 @@ gboolean nsgtk_history_button_press_event(GtkWidget *widget, return TRUE; } + #define GET_WIDGET(x) glade_xml_get_widget(g->xml, (x)) nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) @@ -1582,6 +1605,7 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) gtk_scrolled_window_add_with_viewport(g->history_window->scrolled, GTK_WIDGET(g->history_window->drawing_area)); gtk_widget_show(GTK_WIDGET(g->history_window->drawing_area)); + /* set up URL bar completion */ g->url_bar_completion = gtk_entry_completion_new(); @@ -1613,7 +1637,7 @@ nsgtk_scaffolding *nsgtk_new_scaffolding(struct gui_window *toplevel) nsgtk_history_button_press_event, g->history_window); CONNECT(g->history_window->window, "delete_event", gtk_widget_hide_on_delete, NULL); - + g_signal_connect_after(g->notebook, "page-added", G_CALLBACK(nsgtk_window_tabs_num_changed), g); g_signal_connect_after(g->notebook, "page-removed", @@ -2408,6 +2432,7 @@ void nsgtk_scaffolding_toolbar_init(struct gtk_scaffolding *g) ITEM_MAIN(GLOBALHISTORY, nav, globalhistory); ITEM_MAIN(ADDBOOKMARKS, nav, addbookmarks); ITEM_MAIN(SHOWBOOKMARKS, nav, showbookmarks); + ITEM_MAIN(SHOWCOOKIES, nav, showcookies); ITEM_MAIN(OPENLOCATION, nav, openlocation); ITEM_MAIN(CONTENTS, help, contents); ITEM_MAIN(INFO, help, info); @@ -2437,8 +2462,6 @@ void nsgtk_scaffolding_toolbar_init(struct gtk_scaffolding *g) SENSITIVITY(CONTENTS); SENSITIVITY(DRAWFILE); SENSITIVITY(POSTSCRIPT); - SENSITIVITY(ADDBOOKMARKS); - SENSITIVITY(SHOWBOOKMARKS); SENSITIVITY(NEXTTAB); SENSITIVITY(PREVTAB); SENSITIVITY(CLOSETAB); diff --git a/gtk/gtk_scaffolding.h b/gtk/gtk_scaffolding.h index ddc9fb232..1db576ac1 100644 --- a/gtk/gtk_scaffolding.h +++ b/gtk/gtk_scaffolding.h @@ -73,6 +73,7 @@ typedef enum { GLOBALHISTORY_BUTTON, ADDBOOKMARKS_BUTTON, SHOWBOOKMARKS_BUTTON, + SHOWCOOKIES_BUTTON, OPENLOCATION_BUTTON, NEXTTAB_BUTTON, PREVTAB_BUTTON, @@ -209,6 +210,7 @@ MULTIPROTO(localhistory); MULTIPROTO(globalhistory); MULTIPROTO(addbookmarks); MULTIPROTO(showbookmarks); +MULTIPROTO(showcookies); MULTIPROTO(openlocation); /* tabs menu */ diff --git a/gtk/gtk_theme.c b/gtk/gtk_theme.c index 05421c3d3..98fc03410 100644 --- a/gtk/gtk_theme.c +++ b/gtk/gtk_theme.c @@ -447,6 +447,7 @@ struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s) SET_BUTTON_IMAGE(cachetheme, GLOBALHISTORY, theme) SET_BUTTON_IMAGE(cachetheme, ADDBOOKMARKS, theme) SET_BUTTON_IMAGE(cachetheme, SHOWBOOKMARKS, theme) + SET_BUTTON_IMAGE(cachetheme, SHOWCOOKIES, theme) SET_BUTTON_IMAGE(cachetheme, OPENLOCATION, theme) SET_BUTTON_IMAGE(cachetheme, NEXTTAB, theme) SET_BUTTON_IMAGE(cachetheme, PREVTAB, theme) @@ -571,6 +572,7 @@ void nsgtk_theme_prepare(void) CACHE_IMAGE(GLOBALHISTORY, globalhistory, path); CACHE_IMAGE(ADDBOOKMARKS, addbookmarks, path); CACHE_IMAGE(SHOWBOOKMARKS, showbookmarks, path); + CACHE_IMAGE(SHOWCOOKIES, showcookies, path); CACHE_IMAGE(OPENLOCATION, openlocation, path); CACHE_IMAGE(NEXTTAB, nexttab, path); CACHE_IMAGE(PREVTAB, prevtab, path); diff --git a/gtk/gtk_toolbar.c b/gtk/gtk_toolbar.c index 92bb21f93..ab463a103 100644 --- a/gtk/gtk_toolbar.c +++ b/gtk/gtk_toolbar.c @@ -822,6 +822,7 @@ GtkWidget *nsgtk_toolbar_make_widget(nsgtk_scaffolding *g, MAKE_MENUBUTTON(GLOBALHISTORY, gtkGlobalHistory) MAKE_MENUBUTTON(ADDBOOKMARKS, gtkAddBookMarks) MAKE_MENUBUTTON(SHOWBOOKMARKS, gtkShowBookMarks) + MAKE_MENUBUTTON(SHOWCOOKIES, gtkShowCookies) MAKE_MENUBUTTON(OPENLOCATION, gtkOpenLocation) MAKE_MENUBUTTON(NEXTTAB, gtkNextTab) MAKE_MENUBUTTON(PREVTAB, gtkPrevTab) @@ -986,6 +987,7 @@ DATAHANDLER(localhistory, LOCALHISTORY, window) DATAHANDLER(globalhistory, GLOBALHISTORY, window) DATAHANDLER(addbookmarks, ADDBOOKMARKS, window) DATAHANDLER(showbookmarks, SHOWBOOKMARKS, window) +DATAHANDLER(showcookies, SHOWCOOKIES, window) DATAHANDLER(openlocation, OPENLOCATION, window) DATAHANDLER(nexttab, NEXTTAB, window) DATAHANDLER(prevtab, PREVTAB, window) diff --git a/gtk/gtk_toolbar.h b/gtk/gtk_toolbar.h index a470a4480..7d483578b 100644 --- a/gtk/gtk_toolbar.h +++ b/gtk/gtk_toolbar.h @@ -74,6 +74,7 @@ TOOLPROTO(localhistory); TOOLPROTO(globalhistory); TOOLPROTO(addbookmarks); TOOLPROTO(showbookmarks); +TOOLPROTO(showcookies); TOOLPROTO(openlocation); TOOLPROTO(nexttab); TOOLPROTO(prevtab); diff --git a/gtk/gtk_treeview.c b/gtk/gtk_treeview.c index 6e5643009..20562e3d5 100644 --- a/gtk/gtk_treeview.c +++ b/gtk/gtk_treeview.c @@ -1,5 +1,6 @@ /* * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -20,108 +21,522 @@ * Generic tree handling (implementation). */ +#include <assert.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <stdio.h> #include "desktop/tree.h" +#include "desktop/tree_url_node.h" +#include "desktop/plotters.h" +#include "gtk/gtk_gui.h" +#include "gtk/gtk_plotters.h" +#include "gtk/gtk_treeview.h" +#include "utils/log.h" +#include "utils/utils.h" +struct nsgtk_treeview { + GtkWindow *window; + GtkScrolledWindow *scrolled; + GtkDrawingArea *drawing_area; + int mouse_pressed_x; + int mouse_pressed_y; + browser_mouse_state mouse_state; + struct tree *tree; +}; -/** - * 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) { -} +const char tree_directory_icon_name[] = "directory.png"; +const char tree_content_icon_name[] = "content.png"; +static void nsgtk_tree_redraw_request(int x, int y, int width, int height, + void *data); +static void nsgtk_tree_resized(struct tree *tree, int width, int height, void *data); +static void nsgtk_tree_scroll_visible(int y, int height, void *data); +static void nsgtk_tree_get_window_dimensions(int *width, int *height, void *data); -/** - * 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) { -} +static const struct treeview_table nsgtk_tree_callbacks = { + .redraw_request = nsgtk_tree_redraw_request, + .resized = nsgtk_tree_resized, + .scroll_visible = nsgtk_tree_scroll_visible, + .get_window_dimensions = nsgtk_tree_get_window_dimensions +}; +struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, + GtkWindow *window, GtkScrolledWindow *scrolled, + GtkDrawingArea *drawing_area) +{ + struct nsgtk_treeview *tv; + + tv = malloc(sizeof(struct nsgtk_treeview)); + if (tv == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + return NULL; + } + + tv->window = window; + tv->scrolled = scrolled; + tv->drawing_area = drawing_area; + tv->tree = tree_create(flags, &nsgtk_tree_callbacks, tv); + tv->mouse_state = 0; + + gtk_widget_modify_bg(GTK_WIDGET(drawing_area), GTK_STATE_NORMAL, + &((GdkColor) { 0, 0xffff, 0xffff, 0xffff } )); + +#define CONNECT(obj, sig, callback, ptr) \ + g_signal_connect(G_OBJECT(obj), (sig), G_CALLBACK(callback), (ptr)) + + CONNECT(drawing_area, "expose_event", + nsgtk_tree_window_expose_event, + tv->tree); + CONNECT(drawing_area, "button_press_event", + nsgtk_tree_window_button_press_event, + tv); + CONNECT(drawing_area, "button_release_event", + nsgtk_tree_window_button_release_event, + tv); + CONNECT(drawing_area, "motion_notify_event", + nsgtk_tree_window_motion_notify_event, + tv); + CONNECT(drawing_area, "key_press_event", + nsgtk_tree_window_keypress_event, + tv); + return tv; +} -/** - * Draws a line. - * - * \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(int x, int y, int width, int height) { +void nsgtk_treeview_destroy(struct nsgtk_treeview *tv) +{ + tree_delete(tv->tree); + gtk_widget_destroy(GTK_WIDGET(tv->window)); + free(tv); } +struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv) +{ + return tv->tree; +} -/** - * 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) { +void nsgtk_tree_redraw_request(int x, int y, int width, int height, void *data) +{ + struct nsgtk_treeview *tw = data; + + gtk_widget_queue_draw_area(GTK_WIDGET(tw->drawing_area), + x, y, width, height); } /** - * Draws an elements expansion icon + * Updates the tree owner following a tree resize * - * \param tree the tree to draw the expansion for - * \param element the element to draw the expansion for + * \param tree the tree to update the owner of */ -void tree_draw_node_expansion(struct tree *tree, struct node *node) { +void nsgtk_tree_resized(struct tree *tree, int width, int height, void *data) +{ + struct nsgtk_treeview *tw = data; + + gtk_widget_set_size_request(GTK_WIDGET(tw->drawing_area), + width, height); + return; } - /** - * Recalculates the dimensions of a node element. + * Translates a content_type to the name of a respective icon * - * \param element the element to recalculate + * \param content_type content type + * \param buffer buffer for the icon name */ -void tree_recalculate_node_element(struct node_element *element) { +void tree_icon_name_from_content_type(char *buffer, content_type type) +{ + // TODO: design/acquire icons + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_SPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_DRAW + case CONTENT_DRAW: +#endif +#ifdef WITH_ARTWORKS + case CONTENT_ARTWORKS: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + sprintf(buffer, tree_content_icon_name); + break; + } } /** - * Sets a node element as having a specific sprite. + * Scrolls the tree to make an element visible * - * \param node the node to update - * \param sprite the sprite to use - * \param selected the expanded sprite name to use + * \param y Y coordinate of the element + * \param height height of the element + * \param data user data assigned to the tree on tree creation */ -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded) { +void nsgtk_tree_scroll_visible(int y, int height, void *data) +{ + int y0, y1; + gdouble page; + struct nsgtk_treeview *tw = data; + GtkAdjustment *vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled); + assert(vadj); + + g_object_get(vadj, "page-size", &page, NULL); + + y0 = (int)(gtk_adjustment_get_value(vadj)); + y1 = y0 + page; + + if ((y >= y0) && (y + height <= y1)) + return; + if (y + height > y1) + y0 = y0 + (y + height - y1); + if (y < y0) + y0 = y; + gtk_adjustment_set_value(vadj, y0); } + /** - * Sets a node element as having a folder sprite + * Retrieves the dimensions of the window with the tree * - * \param node the node to update + * \param data user data assigned to the tree on tree creation + * \param width will be updated to window width if not NULL + * \param height will be updated to window height if not NULL */ -void tree_set_node_sprite_folder(struct node *node) { +void nsgtk_tree_get_window_dimensions(int *width, int *height, void *data) +{ + struct nsgtk_treeview *tw = data; + GtkAdjustment *vadj; + GtkAdjustment *hadj; + gdouble page; + + if (width != NULL) { + hadj = gtk_scrolled_window_get_hadjustment(tw->scrolled); + g_object_get(hadj, "page-size", &page, NULL); + *width = page; + } + + if (height != NULL) { + vadj = gtk_scrolled_window_get_vadjustment(tw->scrolled); + g_object_get(vadj, "page-size", &page, NULL); + *height = page; + } +} +/* signal handler functions for a tree window */ +gboolean nsgtk_tree_window_expose_event(GtkWidget *widget, + GdkEventExpose *event, gpointer g) +{ + struct tree *tree = (struct tree *) g; + int x, y, width, height; + + x = event->area.x; + y = event->area.y; + width = event->area.width; + height = event->area.height; + + current_widget = widget; + current_drawable = widget->window; + current_gc = gdk_gc_new(current_drawable); +#ifdef CAIRO_VERSION + current_cr = gdk_cairo_create(current_drawable); +#endif + plot = nsgtk_plotters; + nsgtk_plot_set_scale(1.0);current_widget = widget; + current_drawable = widget->window; + current_gc = gdk_gc_new(current_drawable); +#ifdef CAIRO_VERSION + current_cr = gdk_cairo_create(current_drawable); +#endif + plot = nsgtk_plotters; + nsgtk_plot_set_scale(1.0); + + tree_set_redraw(tree, true); + tree_draw(tree, 0, 0, x, y, width, height); + + current_widget = NULL; + g_object_unref(current_gc); +#ifdef CAIRO_VERSION + cairo_destroy(current_cr); +#endif + + return FALSE; } -/** - * 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, const char *url, - const struct url_data *data) { +void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g) +{ + struct nsgtk_treeview *tw = g; + struct tree *tree = tw->tree; + + if (tree != NULL) + tree_set_redraw(tree, false); } +gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + struct nsgtk_treeview *tw = g; + struct tree *tree = tw->tree; + + gtk_widget_grab_focus(GTK_WIDGET(tw->drawing_area)); + + + tw->mouse_pressed_x = event->x; + tw->mouse_pressed_y = event->y; -/** - * Updates the tree owner following a tree resize - * - * \param tree the tree to update the owner of - */ -void tree_resized(struct tree *tree) { + if (event->type == GDK_2BUTTON_PRESS) + tw->mouse_state = BROWSER_MOUSE_DOUBLE_CLICK; + + switch (event->button) { + case 1: tw->mouse_state |= BROWSER_MOUSE_PRESS_1; break; + case 3: tw->mouse_state |= BROWSER_MOUSE_PRESS_2; break; + } + /* Handle the modifiers too */ + if (event->state & GDK_SHIFT_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_1; + if (event->state & GDK_CONTROL_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_2; + if (event->state & GDK_MOD1_MASK) + tw->mouse_state |= BROWSER_MOUSE_MOD_3; + + tree_mouse_action(tree, tw->mouse_state, event->x, event->y); + + return TRUE; } + +gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + bool alt = event->state & GDK_MOD1_MASK; + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + + /* We consider only button 1 clicks as double clicks. + * If the mouse state is PRESS then we are waiting for a release to emit + * a click event, otherwise just reset the state to nothing*/ + if (tw->mouse_state & BROWSER_MOUSE_DOUBLE_CLICK) { + + if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) + tw->mouse_state ^= BROWSER_MOUSE_PRESS_1; + else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_DOUBLE_CLICK); + + } else if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) + tw->mouse_state ^= + (BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1); + else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2) + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_CLICK_2); + + /* Handle modifiers being removed */ + if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift) + tw->mouse_state ^= BROWSER_MOUSE_MOD_1; + if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl) + tw->mouse_state ^= BROWSER_MOUSE_MOD_2; + if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt) + tw->mouse_state ^= BROWSER_MOUSE_MOD_3; + + + if (tw->mouse_state & + (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_CLICK_2 + | BROWSER_MOUSE_DOUBLE_CLICK)) + tree_mouse_action(tree, tw->mouse_state, + event->x, event->y); + else + tree_drag_end(tree, tw->mouse_state, + tw->mouse_pressed_x, + tw->mouse_pressed_y, + event->x, event->y); + + + tw->mouse_state = 0; + + + return TRUE; +} + +gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget, + GdkEventButton *event, gpointer g) +{ + bool shift = event->state & GDK_SHIFT_MASK; + bool ctrl = event->state & GDK_CONTROL_MASK; + bool alt = event->state & GDK_MOD1_MASK; + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + + + /* Handle modifiers being removed */ + if (tw->mouse_state & BROWSER_MOUSE_MOD_1 && !shift) + tw->mouse_state ^= BROWSER_MOUSE_MOD_1; + if (tw->mouse_state & BROWSER_MOUSE_MOD_2 && !ctrl) + tw->mouse_state ^= BROWSER_MOUSE_MOD_2; + if (tw->mouse_state & BROWSER_MOUSE_MOD_3 && !alt) + tw->mouse_state ^= BROWSER_MOUSE_MOD_3; + + if (tw->mouse_state & BROWSER_MOUSE_PRESS_1) { + /* Start button 1 drag */ + tree_mouse_action(tree, BROWSER_MOUSE_DRAG_1, + tw->mouse_pressed_x, tw->mouse_pressed_y); + /* Replace PRESS with HOLDING and declare drag in progress */ + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_1 | + BROWSER_MOUSE_HOLDING_1); + tw->mouse_state |= BROWSER_MOUSE_DRAG_ON; + return TRUE; + } + else if (tw->mouse_state & BROWSER_MOUSE_PRESS_2){ + /* Start button 2s drag */ + tree_mouse_action(tree, BROWSER_MOUSE_DRAG_2, + tw->mouse_pressed_x, tw->mouse_pressed_y); + /* Replace PRESS with HOLDING and declare drag in progress */ + tw->mouse_state ^= (BROWSER_MOUSE_PRESS_2 | + BROWSER_MOUSE_HOLDING_2); + tw->mouse_state |= BROWSER_MOUSE_DRAG_ON; + return TRUE; + } + + if (tw->mouse_state & (BROWSER_MOUSE_HOLDING_1 | + BROWSER_MOUSE_HOLDING_2)) + tree_mouse_action(tree, tw->mouse_state, event->x, + event->y); + + return TRUE; +} + + +gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, + gpointer g) +{ + struct nsgtk_treeview *tw = (struct nsgtk_treeview *) g; + struct tree *tree = tw->tree; + uint32_t nskey; + double value; + GtkAdjustment *vscroll; + GtkAdjustment *hscroll; + GtkAdjustment *scroll = NULL; + gdouble hpage, vpage; + bool edited; + + nskey = gtk_gui_gdkkey_to_nskey(event); + + + vscroll = gtk_scrolled_window_get_vadjustment(tw->scrolled); + hscroll = gtk_scrolled_window_get_hadjustment(tw->scrolled); + g_object_get(vscroll, "page-size", &vpage, NULL); + g_object_get(hscroll, "page-size", &hpage, NULL); + + + edited = tree_is_edited(tree); + + switch (event->keyval) { + case GDK_Home: + case GDK_KP_Home: + if (edited) + break; + scroll = vscroll; + value = scroll->lower; + break; + + case GDK_End: + case GDK_KP_End: + if (edited) + break; + scroll = vscroll; + value = scroll->upper - vpage; + if (value < scroll->lower) + value = scroll->lower; + break; + + case GDK_Left: + case GDK_KP_Left: + if (edited) + break; + scroll = hscroll; + value = gtk_adjustment_get_value(scroll) - + scroll->step_increment; + if (value < scroll->lower) + value = scroll->lower; + break; + + case GDK_Up: + case GDK_KP_Up: + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) - + scroll->step_increment; + if (value < scroll->lower) + value = scroll->lower; + break; + + case GDK_Right: + case GDK_KP_Right: + if (edited) + break; + scroll = hscroll; + value = gtk_adjustment_get_value(scroll) + + scroll->step_increment; + if (value > scroll->upper - hpage) + value = scroll->upper - hpage; + break; + + case GDK_Down: + case GDK_KP_Down: + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) + + scroll->step_increment; + if (value > scroll->upper - vpage) + value = scroll->upper - vpage; + break; + + case GDK_Page_Up: + case GDK_KP_Page_Up: + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) - + scroll->page_increment; + if (value < scroll->lower) + value = scroll->lower; + break; + + case GDK_Page_Down: + case GDK_KP_Page_Down: + scroll = vscroll; + value = gtk_adjustment_get_value(scroll) + + scroll->page_increment; + if (value > scroll->upper - vpage) + value = scroll->upper - vpage; + break; + default: + tree_keypress(tree, nskey); + return TRUE; + } + + if (scroll != NULL) + gtk_adjustment_set_value(scroll, value); + + tree_keypress(tree, nskey); + + return TRUE; +} diff --git a/gtk/gtk_treeview.h b/gtk/gtk_treeview.h new file mode 100644 index 000000000..655f8223d --- /dev/null +++ b/gtk/gtk_treeview.h @@ -0,0 +1,50 @@ +/* + * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + * Copyright 2009 Paul Blokus <paul_pl@users.sourceforge.net> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * Generic tree handling. + */ + +#ifndef __NSGTK_TREEVIEW_H__ +#define __NSGTK_TREEVIEW_H__ + +#include "desktop/browser.h" + +struct nsgtk_treeview; + +struct nsgtk_treeview *nsgtk_treeview_create(unsigned int flags, + GtkWindow *window, GtkScrolledWindow *scrolled, + GtkDrawingArea *drawing_area); +void nsgtk_treeview_destroy(struct nsgtk_treeview *tv); + +struct tree *nsgtk_treeview_get_tree(struct nsgtk_treeview *tv); + +gboolean nsgtk_tree_window_expose_event(GtkWidget *, GdkEventExpose *, + gpointer g); +void nsgtk_tree_window_hide(GtkWidget *widget, gpointer g); +gboolean nsgtk_tree_window_button_press_event(GtkWidget *widget, + GdkEventButton *event, gpointer g); +gboolean nsgtk_tree_window_button_release_event(GtkWidget *widget, + GdkEventButton *event, gpointer g); +gboolean nsgtk_tree_window_motion_notify_event(GtkWidget *widget, + GdkEventButton *event, gpointer g); +gboolean nsgtk_tree_window_keypress_event(GtkWidget *widget, GdkEventKey *event, + gpointer g); + +#endif /*__NSGTK_TREEVIEW_H__*/ diff --git a/gtk/gtk_window.c b/gtk/gtk_window.c index 509976519..68e636b82 100644 --- a/gtk/gtk_window.c +++ b/gtk/gtk_window.c @@ -606,7 +606,7 @@ gboolean nsgtk_window_scroll_event(GtkWidget *widget, } gboolean nsgtk_window_keypress_event(GtkWidget *widget, GdkEventKey *event, - gpointer data) + gpointer data) { struct gui_window *g = data; uint32_t nskey = gtk_gui_gdkkey_to_nskey(event); diff --git a/gtk/options.h b/gtk/options.h index cb6f0bde2..ac14259d8 100644 --- a/gtk/options.h +++ b/gtk/options.h @@ -34,6 +34,7 @@ extern int option_history_age; extern bool option_hover_urls; extern bool option_focus_new; extern bool option_new_blank; +extern char *option_hotlist_path; extern bool option_source_tab; extern int option_current_theme; @@ -51,6 +52,7 @@ int option_history_age = 0; \ bool option_hover_urls = false; \ bool option_focus_new = false; \ bool option_new_blank = false; \ +char *option_hotlist_path = NULL; \ bool option_source_tab = false;\ int option_current_theme = 0; @@ -68,6 +70,7 @@ int option_current_theme = 0; { "hover_urls", OPTION_BOOL, &option_hover_urls}, \ { "focus_new", OPTION_BOOL, &option_focus_new}, \ { "new_blank", OPTION_BOOL, &option_new_blank}, \ +{ "hotlist_path", OPTION_STRING, &option_hotlist_path}, \ { "source_tab", OPTION_BOOL, &option_source_tab},\ { "current_theme", OPTION_INTEGER, &option_current_theme} diff --git a/gtk/res/cookies.glade b/gtk/res/cookies.glade new file mode 100644 index 000000000..2bb92f372 --- /dev/null +++ b/gtk/res/cookies.glade @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.5 on Tue Jul 28 17:12:17 2009 --> +<glade-interface> + <widget class="GtkWindow" id="wndCookies"> + <property name="title" translatable="yes">NetSurf Cookies</property> + <property name="window_position">GTK_WIN_POS_CENTER</property> + <property name="default_width">600</property> + <property name="default_height">500</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_UTILITY</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="border_width">2</property> + <property name="spacing">2</property> + <child> + <widget class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menuitem2"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu2"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="delete_selected"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Deletes selected nodes</property> + <property name="label" translatable="yes">_Delete</property> + <property name="use_underline">True</property> + <accelerator key="Delete" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="delete_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">D_elete all</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="select_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Select all</property> + <property name="use_underline">True</property> + <accelerator key="A" modifiers="GDK_MOD1_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="clear_selection"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Clear selection</property> + <property name="use_underline">True</property> + <accelerator key="U" modifiers="GDK_MOD1_MASK" signal="activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem3"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu4"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="expand"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Expand</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu5"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="expand_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_domains"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Domains</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_cookies"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Cookies</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse"> + <property name="visible">True</property> + <property name="label" translatable="yes">Collapse</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu6"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="collapse_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_domains"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Domains</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_cookies"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Cookies</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="cookiesScrolled"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkViewport" id="cookiesViewport"> + <property name="visible">True</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <child> + <widget class="GtkDrawingArea" id="cookiesDrawingArea"> + <property name="visible">True</property> + <property name="app_paintable">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gtk/res/history.glade b/gtk/res/history.glade index e44edecd5..5c6e06424 100644 --- a/gtk/res/history.glade +++ b/gtk/res/history.glade @@ -8,306 +8,190 @@ <property name="default_width">600</property> <property name="default_height">500</property> <property name="type_hint">utility</property> - <signal name="delete_event" handler="gtk_widget_hide"/> <child> <widget class="GtkVBox" id="vbox1"> <property name="visible">True</property> <property name="border_width">2</property> <property name="spacing">2</property> <child> - <widget class="GtkHPaned" id="hpaned2"> + <widget class="GtkMenuBar" id="menubar1"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="position">234</property> <child> - <widget class="GtkVBox" id="vbox28"> + <widget class="GtkMenuItem" id="menuitem1"> <property name="visible">True</property> - <property name="spacing">2</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> <child> - <widget class="GtkHBox" id="hbox2"> + <widget class="GtkMenu" id="menu1"> <property name="visible">True</property> - <property name="spacing">2</property> - <child> - <widget class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="label" translatable="yes">Sort by</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">1</property> - <property name="position">0</property> - </packing> - </child> <child> - <widget class="GtkComboBox" id="comboSort"> + <widget class="GtkMenuItem" id="export"> <property name="visible">True</property> - <property name="items" translatable="yes">Name -Last Visited -Number of Visits</property> - </widget> - <packing> - <property name="position">1</property> - </packing> - </child> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <widget class="GtkScrolledWindow" id="windowDomain"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">1</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <property name="shadow_type">in</property> - <child> - <widget class="GtkTreeView" id="treeDomain"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="rules_hint">True</property> + <property name="label" translatable="yes">_Export</property> + <property name="use_underline">True</property> + <accelerator key="E" modifiers="GDK_CONTROL_MASK" signal="activate"/> </widget> </child> </widget> - <packing> - <property name="position">1</property> - </packing> </child> </widget> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> </child> <child> - <widget class="GtkVBox" id="vbox2"> + <widget class="GtkMenuItem" id="menuitem2"> <property name="visible">True</property> - <property name="spacing">2</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> <child> - <widget class="GtkHBox" id="hbox1"> + <widget class="GtkMenu" id="menu2"> <property name="visible">True</property> <child> - <widget class="GtkLabel" id="label2"> + <widget class="GtkMenuItem" id="delete_selected"> <property name="visible">True</property> - <property name="label" translatable="yes"><b>_Search for</b></property> - <property name="use_markup">True</property> + <property name="tooltip" translatable="yes">Deletes selected nodes</property> + <property name="label" translatable="yes">_Delete</property> <property name="use_underline">True</property> + <accelerator key="Delete" modifiers="GDK_CONTROL_MASK" signal="activate"/> </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">1</property> - <property name="position">0</property> - </packing> </child> <child> - <widget class="GtkEntry" id="entrySearch"> + <widget class="GtkMenuItem" id="delete_all"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">out</property> + <property name="label" translatable="yes">D_elete all</property> + <property name="use_underline">True</property> </widget> - <packing> - <property name="position">1</property> - </packing> </child> <child> - <widget class="GtkButton" id="buttonClearSearch"> + <widget class="GtkMenuItem" id="select_all"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="focus_on_click">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <child> - <widget class="GtkImage" id="image1"> - <property name="visible">True</property> - <property name="tooltip" translatable="yes">Clear the search entry</property> - <property name="stock">gtk-clear</property> - <property name="icon-size">1</property> - </widget> - </child> + <property name="label" translatable="yes">_Select all</property> + <property name="use_underline">True</property> + <accelerator key="A" modifiers="GDK_MOD1_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="clear_selection"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Clear selection</property> + <property name="use_underline">True</property> + <accelerator key="U" modifiers="GDK_MOD1_MASK" signal="activate"/> </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> </child> </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem3"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> <child> - <widget class="GtkScrolledWindow" id="windowSites"> + <widget class="GtkMenu" id="menu4"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="border_width">1</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <property name="shadow_type">in</property> <child> - <widget class="GtkTreeView" id="treeHistory"> + <widget class="GtkMenuItem" id="expand"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="rules_hint">True</property> - <property name="show_expanders">False</property> + <property name="label" translatable="yes">_Expand</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu5"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="expand_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_directories"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Directories</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_addresses"> + <property name="visible">True</property> + <property name="label" translatable="yes">Add_resses</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Collapse</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu6"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="collapse_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_directories"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Directories</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_addresses"> + <property name="visible">True</property> + <property name="label" translatable="yes">Add_resses</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> </widget> </child> </widget> - <packing> - <property name="position">1</property> - </packing> </child> </widget> - <packing> - <property name="resize">False</property> - <property name="shrink">True</property> - </packing> + </child> + <child> + <widget class="GtkMenuItem" id="launch"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Launches the selected addresses</property> + <property name="label" translatable="yes">_Launch</property> + <property name="use_underline">True</property> + </widget> </child> </widget> <packing> - <property name="position">0</property> + <property name="expand">False</property> </packing> </child> <child> - <widget class="GtkHBox" id="hbox3"> + <widget class="GtkScrolledWindow" id="globalHistoryScrolled"> <property name="visible">True</property> - <property name="spacing">5</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> <child> - <widget class="GtkTable" id="table1"> + <widget class="GtkViewport" id="globalHistoryViewport"> <property name="visible">True</property> - <property name="border_width">1</property> - <property name="n_rows">3</property> - <property name="n_columns">2</property> - <property name="column_spacing">5</property> - <property name="row_spacing">5</property> - <child> - <widget class="GtkLabel" id="labelHistoryAddress"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">http://netsurf.sf.net/</property> - <property name="ellipsize">middle</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelHistoryLastVisit"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Fri Aug 09, 2006</property> - <property name="ellipsize">end</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelHistoryVisits"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">2</property> - <property name="ellipsize">end</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options"></property> - </packing> - </child> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> <child> - <widget class="GtkLabel" id="label119"> + <widget class="GtkDrawingArea" id="globalHistoryDrawingArea"> <property name="visible">True</property> - <property name="xalign">1</property> - <property name="label" translatable="yes">Number of visits</property> - </widget> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_EXPAND</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label118"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="label" translatable="yes">Last visited</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_EXPAND</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label117"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="label" translatable="yes">Address</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_EXPAND</property> - </packing> - </child> - </widget> - <packing> - <property name="position">0</property> - </packing> - </child> - <child> - <widget class="GtkFrame" id="imageFrame"> - <property name="label_xalign">1</property> - <property name="label_yalign">1</property> - <property name="shadow_type">in</property> - <child> - <widget class="GtkImage" id="imageThumbnail"> - <property name="width_request">100</property> - <property name="height_request">86</property> - <property name="visible">True</property> - <property name="stock">gtk-file</property> - <property name="icon-size">6</property> + <property name="app_paintable">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property> </widget> </child> - <child> - <placeholder/> - <packing> - <property name="type">label_item</property> - </packing> - </child> </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">1</property> - <property name="position">1</property> - </packing> </child> </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> </child> </widget> </child> diff --git a/gtk/res/hotlist.glade b/gtk/res/hotlist.glade new file mode 100644 index 000000000..5a3b8f1d0 --- /dev/null +++ b/gtk/res/hotlist.glade @@ -0,0 +1,218 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--Generated with glade3 3.4.5 on Wed Jul 29 03:02:21 2009 --> +<glade-interface> + <widget class="GtkWindow" id="wndHotlist"> + <property name="title" translatable="yes">NetSurf Hotlist</property> + <property name="window_position">GTK_WIN_POS_CENTER</property> + <property name="default_width">600</property> + <property name="default_height">500</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_UTILITY</property> + <child> + <widget class="GtkVBox" id="vbox1"> + <property name="visible">True</property> + <property name="border_width">2</property> + <property name="spacing">2</property> + <child> + <widget class="GtkMenuBar" id="menubar1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="menuitem1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu1"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="export"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Export</property> + <property name="use_underline">True</property> + <accelerator key="E" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="new_folder"> + <property name="visible">True</property> + <property name="label" translatable="yes">New _folder</property> + <property name="use_underline">True</property> + <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="new_entry"> + <property name="visible">True</property> + <property name="label" translatable="yes">New _entry</property> + <property name="use_underline">True</property> + <accelerator key="N" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem2"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu2"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="edit_selected"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="delete_selected"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Deletes selected nodes</property> + <property name="label" translatable="yes">_Delete</property> + <property name="use_underline">True</property> + <accelerator key="Delete" modifiers="GDK_CONTROL_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="select_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Select all</property> + <property name="use_underline">True</property> + <accelerator key="A" modifiers="GDK_MOD1_MASK" signal="activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="clear_selection"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Clear selection</property> + <property name="use_underline">True</property> + <accelerator key="U" modifiers="GDK_MOD1_MASK" signal="activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="menuitem3"> + <property name="visible">True</property> + <property name="label" translatable="yes">_View</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu4"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="expand"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Expand</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu5"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="expand_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_directories"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Directories</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="expand_addresses"> + <property name="visible">True</property> + <property name="label" translatable="yes">Add_resses</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Collapse</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="menu6"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="collapse_all"> + <property name="visible">True</property> + <property name="label" translatable="yes">_All</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_directories"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Directories</property> + <property name="use_underline">True</property> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="collapse_addresses"> + <property name="visible">True</property> + <property name="label" translatable="yes">Add_resses</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="launch"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Launches the selected addresses</property> + <property name="label" translatable="yes">_Launch</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="hotlistScrolled"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkViewport" id="hotlistViewport"> + <property name="visible">True</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <child> + <widget class="GtkDrawingArea" id="hotlistDrawingArea"> + <property name="visible">True</property> + <property name="app_paintable">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/gtk/res/icons b/gtk/res/icons new file mode 120000 index 000000000..509ccd1d2 --- /dev/null +++ b/gtk/res/icons @@ -0,0 +1 @@ +../../!NetSurf/Resources/Icons/
\ No newline at end of file diff --git a/gtk/res/ssl.glade b/gtk/res/ssl.glade index 98d9f9bf3..5d0570987 100644 --- a/gtk/res/ssl.glade +++ b/gtk/res/ssl.glade @@ -6,6 +6,8 @@ <property name="border_width">1</property> <property name="title" translatable="yes">SSL certificate problem</property> <property name="modal">True</property> + <property name="default_width">500</property> + <property name="default_height">250</property> <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> <child internal-child="vbox"> <widget class="GtkVBox" id="dialog-vbox3"> @@ -50,17 +52,22 @@ <property name="visible">True</property> <property name="left_padding">12</property> <child> - <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <widget class="GtkScrolledWindow" id="SSLScrolled"> <property name="visible">True</property> <property name="can_focus">True</property> <property name="shadow_type">GTK_SHADOW_IN</property> <child> - <widget class="GtkTextView" id="textview1"> - <property name="height_request">200</property> + <widget class="GtkViewport" id="SSLViewport"> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">False</property> - <property name="text" translatable="yes">(not implemented)</property> + <property name="resize_mode">GTK_RESIZE_QUEUE</property> + <child> + <widget class="GtkDrawingArea" id="SSLDrawingArea"> + <property name="visible">True</property> + <property name="app_paintable">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_STRUCTURE_MASK</property> + </widget> + </child> </widget> </child> </widget> diff --git a/riscos/configure/con_home.c b/riscos/configure/con_home.c index afced9557..fbe1b7039 100644 --- a/riscos/configure/con_home.c +++ b/riscos/configure/con_home.c @@ -22,6 +22,7 @@ #include "riscos/gui.h" #include "riscos/menus.h" #include "riscos/options.h" +#include "riscos/url_suggest.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "riscos/configure.h" @@ -42,16 +43,13 @@ static bool ro_gui_options_home_ok(wimp_w w); bool ro_gui_options_home_initialise(wimp_w w) { - int suggestions; - /* set the current values */ ro_gui_set_icon_string(w, HOME_URL_FIELD, option_homepage_url ? option_homepage_url : "", true); ro_gui_set_icon_selected_state(w, HOME_OPEN_STARTUP, option_open_browser_at_startup); - global_history_get_recent(&suggestions); ro_gui_set_icon_shaded_state(w, - HOME_URL_GRIGHT, (suggestions <= 0)); + HOME_URL_GRIGHT, !ro_gui_url_suggest_prepare_menu()); /* initialise all functions for a newly created window */ ro_gui_wimp_event_register_menu_gright(w, HOME_URL_FIELD, diff --git a/riscos/cookies.c b/riscos/cookies.c index 312f2e479..b0c035adc 100644 --- a/riscos/cookies.c +++ b/riscos/cookies.c @@ -1,5 +1,6 @@ /* * Copyright 2006 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -44,174 +45,243 @@ #include "utils/url.h" #include "utils/utils.h" -static bool ro_gui_cookies_click(wimp_pointer *pointer); -static struct node *ro_gui_cookies_find(const char *url); +static void ro_gui_cookies_menu_prepare(wimp_w window, wimp_menu *menu); +static bool ro_gui_cookies_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); +static void ro_gui_cookies_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); -/* The history window, toolbar and plot origins */ -static wimp_w cookies_window; -struct tree *cookies_tree; -static bool cookies_init; +/* The RISC OS cookie window, toolbar and treeview data. */ + +static struct ro_cookies_window { + wimp_w window; + struct toolbar *toolbar; + ro_treeview *tv; + wimp_menu *menu; +} cookies_window; /** - * Initialise cookies tree + * Pre-Initialise the cookies tree. This is called for things that + * need to be done at the gui_init() stage, such as loading templates. */ -void ro_gui_cookies_initialise(void) + +void ro_gui_cookies_preinitialise(void) { - /* create our window */ - cookies_window = ro_gui_dialog_create("tree"); - ro_gui_set_window_title(cookies_window, + /* Create our window. */ + + cookies_window.window = ro_gui_dialog_create("tree"); + ro_gui_set_window_title(cookies_window.window, messages_get("Cookies")); - ro_gui_wimp_event_register_redraw_window(cookies_window, - ro_gui_tree_redraw); - ro_gui_wimp_event_register_open_window(cookies_window, - ro_gui_tree_open); - ro_gui_wimp_event_register_mouse_click(cookies_window, - ro_gui_cookies_click); - - /* Create an empty tree */ - cookies_tree = calloc(sizeof(struct tree), 1); - if (!cookies_tree) { - warn_user("NoMemory", 0); - return; - } - cookies_tree->root = tree_create_folder_node(NULL, "Root"); - if (!cookies_tree->root) { - warn_user("NoMemory", 0); - free(cookies_tree); - cookies_tree = NULL; +} + +/** + * Initialise cookies tree, at the gui_init2() stage. + */ + +void ro_gui_cookies_postinitialise(void) +{ + /* Create our toolbar. */ + + cookies_window.toolbar = ro_gui_theme_create_toolbar(NULL, + THEME_COOKIES_TOOLBAR); + if (cookies_window.toolbar) + ro_gui_theme_attach_toolbar(cookies_window.toolbar, + cookies_window.window); + + /* Create the treeview with the window and toolbar. */ + + cookies_window.tv = ro_treeview_create(cookies_window.window, + cookies_window.toolbar, cookies_get_tree_flags()); + if (cookies_window.tv == NULL) { + LOG(("Failed to allocate treeview")); return; } - cookies_tree->root->expanded = true; - cookies_tree->handle = (int)cookies_window; - cookies_tree->movable = false; - cookies_tree->no_drag = true; - ro_gui_wimp_event_set_user_data(cookies_window, - cookies_tree); - ro_gui_wimp_event_register_keypress(cookies_window, - ro_gui_tree_keypress); - - /* Create our toolbar */ - cookies_tree->toolbar = ro_gui_theme_create_toolbar(NULL, - THEME_COOKIES_TOOLBAR); - if (cookies_tree->toolbar) - ro_gui_theme_attach_toolbar(cookies_tree->toolbar, - cookies_window); - - cookies_init = true; - urldb_iterate_cookies(cookies_update); - cookies_init = false; - tree_initialise(cookies_tree); + + /* Initialise the cookies into the tree. */ + + cookies_initialise(ro_treeview_get_tree(cookies_window.tv)); + + + /* Build the cookies window menu. */ + + static const struct ns_menu cookies_definition = { + "Cookies", { + { "Cookies", NO_ACTION, 0 }, + { "Cookies.Expand", TREE_EXPAND_ALL, 0 }, + { "Cookies.Expand.All", TREE_EXPAND_ALL, 0 }, + { "Cookies.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, + { "Cookies.Expand.Links", TREE_EXPAND_LINKS, 0 }, + { "Cookies.Collapse", TREE_COLLAPSE_ALL, 0 }, + { "Cookies.Collapse.All", TREE_COLLAPSE_ALL, 0 }, + { "Cookies.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, + { "Cookies.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, + { "Cookies.Toolbars", NO_ACTION, 0 }, + { "_Cookies.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, + { "Cookies.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 }, + { "Selection", TREE_SELECTION, 0 }, + { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, + { "SelectAll", TREE_SELECT_ALL, 0 }, + { "Clear", TREE_CLEAR_SELECTION, 0 }, + {NULL, 0, 0} + } + }; + cookies_window.menu = ro_gui_menu_define_menu(&cookies_definition); + + ro_gui_wimp_event_register_window_menu(cookies_window.window, + cookies_window.menu, ro_gui_cookies_menu_prepare, + ro_gui_cookies_menu_select, NULL, + ro_gui_cookies_menu_warning, false); } +/** + * \TODO - Open the cookies window. + * + */ + +void ro_gui_cookies_open(void) +{ + tree_set_redraw(ro_treeview_get_tree(cookies_window.tv), true); + + if (!ro_gui_dialog_open_top(cookies_window.window, + cookies_window.toolbar, 600, 800)) { + ro_treeview_set_origin(cookies_window.tv, 0, + -(ro_gui_theme_toolbar_height( + cookies_window.toolbar))); + } +} /** - * Respond to a mouse click + * Prepare the cookies menu for opening * - * \param pointer the pointer state - * \return true to indicate click handled + * \param window The window owning the menu. + * \param *menu The menu about to be opened. */ -bool ro_gui_cookies_click(wimp_pointer *pointer) + +void ro_gui_cookies_menu_prepare(wimp_w window, wimp_menu *menu) { - ro_gui_tree_click(pointer, cookies_tree); - if (pointer->buttons == wimp_CLICK_MENU) - ro_gui_menu_create(cookies_menu, pointer->pos.x, - pointer->pos.y, pointer->w); - else - ro_gui_menu_prepare_action(pointer->w, TREE_SELECTION, false); - return true; + bool selection; + + selection = ro_treeview_has_selection(cookies_window.tv); + + ro_gui_menu_set_entry_shaded(cookies_window.menu, TREE_SELECTION, + !selection); + ro_gui_menu_set_entry_shaded(cookies_window.menu, TREE_CLEAR_SELECTION, + !selection); + + ro_gui_menu_set_entry_shaded(cookies_window.menu, TOOLBAR_BUTTONS, + (cookies_window.toolbar == NULL || + cookies_window.toolbar->editor)); + ro_gui_menu_set_entry_ticked(cookies_window.menu, TOOLBAR_BUTTONS, + (cookies_window.toolbar != NULL && + (cookies_window.toolbar->display_buttons || + (cookies_window.toolbar->editor)))); + + ro_gui_menu_set_entry_shaded(cookies_window.menu, TOOLBAR_EDIT, + cookies_window.toolbar == NULL); + ro_gui_menu_set_entry_ticked(cookies_window.menu, TOOLBAR_EDIT, + (cookies_window.toolbar != NULL && + cookies_window.toolbar->editor)); } +/** + * Handle submenu warnings for the cookies menu + * + * \param window The window owning the menu. + * \param *menu The menu to which the warning applies. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. + */ + +void ro_gui_cookies_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) +{ + /* Do nothing */ +} /** - * Perform cookie addition + * Handle selections from the cookies menu * - * \param data Cookie data for a domain, or NULL - * \return true (for urldb_iterate_entries) + * \param window The window owning the menu. + * \param *menu The menu from which the selection was made. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. + * \return true if action accepted; else false. */ -bool cookies_update(const char *domain, const struct cookie_data *data) + +bool ro_gui_cookies_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) { - struct node *parent; - struct node *node = NULL; - struct node *child; - struct node *add; - const struct cookie_data *cookie = NULL; - bool expanded; - - assert(domain); - - /* check if we're a domain, and add get the first cookie */ - if (data) - for (cookie = data; cookie->prev; cookie = cookie->prev); - - if (!cookies_init) { - node = ro_gui_cookies_find(domain); - if (node) { - /* mark as deleted so we don't remove the cookies */ - expanded = node->expanded; - for (child = node->child; child; child = child->next) - child->deleted = true; - if (node->child) - tree_delete_node(cookies_tree, node->child, - true); - /* deleting will have contracted our node */ - node->expanded = expanded; - } - if (!data) { - if (!node) - return true; - tree_delete_node(cookies_tree, node, false); - tree_handle_node_changed(cookies_tree, - cookies_tree->root, true, false); - return true; - } + switch (action) { + case TREE_EXPAND_ALL: + cookies_expand_all(); + return true; + case TREE_EXPAND_FOLDERS: + cookies_expand_domains(); + return true; + case TREE_EXPAND_LINKS: + cookies_expand_cookies(); + return true; + case TREE_COLLAPSE_ALL: + cookies_collapse_all(); + return true; + case TREE_COLLAPSE_FOLDERS: + cookies_collapse_domains(); + return true; + case TREE_COLLAPSE_LINKS: + cookies_collapse_cookies(); + return true; + case TREE_SELECTION_DELETE: + cookies_delete_selected(); + return true; + case TREE_SELECT_ALL: + cookies_select_all(); + return true; + case TREE_CLEAR_SELECTION: + cookies_clear_selection(); + return true; + default: + return false; } - if (!node) { - for (parent = cookies_tree->root->child; parent; - parent = parent->next) { - if (strcmp(domain, parent->data.text) == 0) - break; - } - if (!parent) { - node = tree_create_folder_node(cookies_tree->root, - domain); - } else { - node = parent; - } - } - if (!node) - return true; - node->editable = false; + return false; +} - for (; cookie; cookie = cookie->next) { - add = tree_create_cookie_node(node, cookie); - if (add && !cookies_init) - tree_handle_node_changed(cookies_tree, add, - true, false); - } - if (!cookies_init) { - tree_handle_node_changed(cookies_tree, node, - true, false); - tree_redraw_area(cookies_tree, - node->box.x - NODE_INSTEP, - 0, NODE_INSTEP, 16384); - } - return true; +/** + * Update the theme details of the cookies window. + */ + +void ro_gui_cookies_update_theme(void) +{ + ro_treeview_update_theme(cookies_window.tv); } /** - * Find an entry in the cookie tree + * Check if a particular window handle is the cookies window * - * \param url The URL to find - * \return Pointer to node, or NULL if not found + * \param window the window in question + * \return true if this window is the cookies */ -struct node *ro_gui_cookies_find(const char *url) + +bool ro_gui_cookies_check_window(wimp_w window) { - struct node *node; +/* SF if (cookies_window.w == window) + return true; + else*/ + return false; +} - for (node = cookies_tree->root->child; node; node = node->next) { - if (!strcmp(url, node->data.text)) - return node; - } - return NULL; +/** + * Check if a particular menu handle is the cookies menu + * + * \param *menu The menu in question. + * \return true if this menu is the cookies menu + */ + +bool ro_gui_cookies_check_menu(wimp_menu *menu) +{ + if (cookies_window.menu == menu) + return true; + else + return false; } + diff --git a/riscos/cookies.h b/riscos/cookies.h index b6dbe7bc6..3539ed9a0 100644 --- a/riscos/cookies.h +++ b/riscos/cookies.h @@ -1,5 +1,6 @@ /* * Copyright 2006 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -23,6 +24,13 @@ #ifndef _NETSURF_RISCOS_COOKIES_H_ #define _NETSURF_RISCOS_COOKIES_H_ -void ro_gui_cookies_initialise(void); +void ro_gui_cookies_preinitialise(void); +void ro_gui_cookies_postinitialise(void); +bool ro_gui_cookies_check_window(wimp_w window); +bool ro_gui_cookies_check_menu(wimp_menu *menu); + +void ro_gui_cookies_open(void); +void ro_gui_cookies_update_theme(void); #endif + diff --git a/riscos/dialog.c b/riscos/dialog.c index f0520eb73..b4195a5b7 100644 --- a/riscos/dialog.c +++ b/riscos/dialog.c @@ -39,11 +39,14 @@ #include "riscos/dialog.h" #include "riscos/global_history.h" #include "riscos/gui.h" +#include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/options.h" #include "riscos/save.h" +#include "riscos/sslcert.h" #include "riscos/theme.h" #include "riscos/url_complete.h" +#include "riscos/url_suggest.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "riscos/wimputils.h" @@ -106,18 +109,6 @@ void ro_gui_dialog_init(void) /* 401 login window */ ro_gui_401login_init(); - /* certificate verification window */ - ro_gui_cert_init(); - - /* hotlist window */ - ro_gui_hotlist_initialise(); - - /* global history window */ - ro_gui_global_history_initialise(); - - /* cookies window */ - ro_gui_cookies_initialise(); - /* theme installation */ dialog_theme_install = ro_gui_dialog_create("theme_inst"); ro_gui_wimp_event_register_cancel(dialog_theme_install, @@ -150,24 +141,6 @@ void ro_gui_dialog_init(void) dialog_objinfo = ro_gui_dialog_create("objectinfo"); ro_gui_wimp_event_set_help_prefix(dialog_objinfo, "HelpObjInfo"); - /* hotlist folder editing */ - dialog_folder = ro_gui_dialog_create("new_folder"); - ro_gui_wimp_event_register_text_field(dialog_folder, ICON_FOLDER_NAME); - ro_gui_wimp_event_register_cancel(dialog_folder, ICON_FOLDER_CANCEL); - ro_gui_wimp_event_register_ok(dialog_folder, ICON_FOLDER_OK, - ro_gui_hotlist_dialog_apply); - ro_gui_wimp_event_set_help_prefix(dialog_folder, "HelpHotFolder"); - - /* hotlist entry editing */ - dialog_entry = ro_gui_dialog_create("new_entry"); - ro_gui_wimp_event_register_text_field(dialog_entry, ICON_ENTRY_NAME); - ro_gui_wimp_event_register_menu_gright(dialog_entry, ICON_ENTRY_URL, - ICON_ENTRY_RECENT, url_suggest_menu); - ro_gui_wimp_event_register_cancel(dialog_entry, ICON_ENTRY_CANCEL); - ro_gui_wimp_event_register_ok(dialog_entry, ICON_ENTRY_OK, - ro_gui_hotlist_dialog_apply); - ro_gui_wimp_event_set_help_prefix(dialog_entry, "HelpHotEntry"); - /* save as */ dialog_saveas = ro_gui_saveas_create("saveas"); ro_gui_wimp_event_register_button(dialog_saveas, ICON_SAVE_ICON, @@ -204,6 +177,22 @@ void ro_gui_dialog_init(void) ro_gui_wimp_event_register_ok(dialog_zoom, ICON_ZOOM_OK, ro_gui_dialog_zoom_apply); ro_gui_wimp_event_set_help_prefix(dialog_zoom, "HelpScaleView"); + + /* Treeview initialisation has moved to the end, to allow any + * associated dialogues to be set up first. + */ + + /* certificate verification window */ + ro_gui_cert_preinitialise(); + + /* hotlist window */ + ro_gui_hotlist_preinitialise(); + + /* global history window */ + ro_gui_global_history_preinitialise(); + + /* cookies window */ + ro_gui_cookies_preinitialise(); } @@ -638,15 +627,25 @@ void ro_gui_dialog_add_persistent(wimp_w parent, wimp_w w) { */ void ro_gui_dialog_close_persistent(wimp_w parent) { - int i; - - /* Check our mappings - */ + int i; + wimp_w w; + + /* Check our mappings. + * + * The window handle is copied into w before proceeding, as + * ro_gui_dialog_close() will NULL persistent_dialog[i].dialog as + * part of the closing process. This would mean that the subsequent + * event dispatch would fail. (These events are logged to allow + * side effects to be investigated -- this code hasn't worked before). + */ for (i = 0; i < MAX_PERSISTENT; i++) { if (persistent_dialog[i].parent == parent && persistent_dialog[i].dialog != NULL) { - ro_gui_dialog_close(persistent_dialog[i].dialog); - ro_gui_wimp_event_close_window(persistent_dialog[i].dialog); + w = persistent_dialog[i].dialog; + ro_gui_dialog_close(w); + if (ro_gui_wimp_event_close_window(w)) + LOG(("Persistent dialog close event: 0x%x", + (unsigned) w)); persistent_dialog[i].parent = NULL; persistent_dialog[i].dialog = NULL; } @@ -710,7 +709,6 @@ bool ro_gui_dialog_openurl_apply(wimp_w w) { res = url_normalize(url, &url2); if (res == URL_FUNC_OK) { browser_window_create(url2, 0, 0, true, false); - global_history_add_recent(url2); free(url2); return true; } @@ -724,10 +722,8 @@ bool ro_gui_dialog_openurl_apply(wimp_w w) { void ro_gui_dialog_prepare_open_url(void) { - int suggestions; ro_gui_set_icon_string(dialog_openurl, ICON_OPENURL_URL, "", true); - global_history_get_recent(&suggestions); ro_gui_set_icon_shaded_state(dialog_openurl, - ICON_OPENURL_MENU, (suggestions <= 0)); + ICON_OPENURL_MENU, !ro_gui_url_suggest_prepare_menu()); ro_gui_wimp_event_memorise(dialog_openurl); } diff --git a/riscos/global_history.c b/riscos/global_history.c index 5cccae0b4..e9f5ea6ad 100644 --- a/riscos/global_history.c +++ b/riscos/global_history.c @@ -1,5 +1,6 @@ /* * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -29,12 +30,14 @@ #include "oslib/wimp.h" #include "oslib/wimpspriteop.h" #include "content/urldb.h" +#include "desktop/history_global_core.h" #include "desktop/tree.h" #include "riscos/dialog.h" #include "riscos/global_history.h" #include "riscos/gui.h" #include "riscos/menus.h" #include "riscos/options.h" +#include "riscos/save.h" #include "riscos/theme.h" #include "riscos/treeview.h" #include "riscos/wimp.h" @@ -44,401 +47,260 @@ #include "utils/url.h" #include "utils/utils.h" -#define MAXIMUM_URL_LENGTH 1024 -#define MAXIMUM_BASE_NODES 16 +static void ro_gui_global_history_menu_prepare(wimp_w window, wimp_menu *menu); +static bool ro_gui_global_history_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); +static void ro_gui_global_history_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); -static struct node *global_history_base_node[MAXIMUM_BASE_NODES]; -static int global_history_base_node_time[MAXIMUM_BASE_NODES]; -static int global_history_base_node_count = 0; +/* The RISC OS global history window, toolbar and treeview data */ -static char *global_history_recent_url[GLOBAL_HISTORY_RECENT_URLS]; -static int global_history_recent_count = 0; - -static bool global_history_init; - -static bool ro_gui_global_history_click(wimp_pointer *pointer); -static void ro_gui_global_history_initialise_nodes(void); -static void ro_gui_global_history_initialise_node(const char *title, - time_t base, int days_back); -static struct node *ro_gui_global_history_find(const char *url); -static bool global_history_add_internal(const char *url, - const struct url_data *data); - -/* The history window, toolbar and plot origins */ -static wimp_w global_history_window; -struct tree *global_history_tree; +static struct ro_global_history_window { + wimp_w window; + struct toolbar *toolbar; + ro_treeview *tv; + wimp_menu *menu; +} global_history_window; /** - * Initialise global history tree + * Pre-Initialise the global history tree. This is called for things that + * need to be done at the gui_init() stage, such as loading templates. */ -void ro_gui_global_history_initialise(void) + +void ro_gui_global_history_preinitialise(void) { - char s[MAXIMUM_URL_LENGTH]; - FILE *fp; + /* Create our window. */ - /* create our window */ - global_history_window = ro_gui_dialog_create("tree"); - ro_gui_set_window_title(global_history_window, + global_history_window.window = ro_gui_dialog_create("tree"); + ro_gui_set_window_title(global_history_window.window, messages_get("GlobalHistory")); - ro_gui_wimp_event_register_redraw_window(global_history_window, - ro_gui_tree_redraw); - ro_gui_wimp_event_register_open_window(global_history_window, - ro_gui_tree_open); - ro_gui_wimp_event_register_mouse_click(global_history_window, - ro_gui_global_history_click); - - /* Create an empty tree */ - global_history_tree = calloc(sizeof(struct tree), 1); - if (!global_history_tree) { - warn_user("NoMemory", 0); - return; - } - global_history_tree->root = tree_create_folder_node(NULL, "Root"); - if (!global_history_tree->root) { - warn_user("NoMemory", 0); - free(global_history_tree); - global_history_tree = NULL; - return; - } - global_history_tree->root->expanded = true; - ro_gui_global_history_initialise_nodes(); - global_history_tree->handle = (int)global_history_window; - global_history_tree->movable = false; - ro_gui_wimp_event_set_user_data(global_history_window, - global_history_tree); - ro_gui_wimp_event_register_keypress(global_history_window, - ro_gui_tree_keypress); - - /* Create our toolbar */ - global_history_tree->toolbar = ro_gui_theme_create_toolbar(NULL, - THEME_HISTORY_TOOLBAR); - if (global_history_tree->toolbar) - ro_gui_theme_attach_toolbar(global_history_tree->toolbar, - global_history_window); - - /* load recent URLs */ - fp = fopen(option_recent_path, "r"); - if (!fp) - LOG(("Failed to open file '%s' for reading", - option_recent_path)); - else { - while (fgets(s, MAXIMUM_URL_LENGTH, fp)) { - if (s[strlen(s) - 1] == '\n') - s[strlen(s) - 1] = '\0'; - global_history_add_recent(s); - } - fclose(fp); - } - - global_history_init = true; - urldb_iterate_entries(global_history_add_internal); - global_history_init = false; - tree_initialise(global_history_tree); } /** - * Initialises the base nodes + * Initialise global history tree, at the gui_init2() stage. */ -void ro_gui_global_history_initialise_nodes(void) -{ - struct tm *full_time; - time_t t; - int weekday; - int i; - - /* get the current time */ - t = time(NULL); - if (t == -1) - return; - /* get the time at the start of today */ - full_time = localtime(&t); - weekday = full_time->tm_wday; - full_time->tm_sec = 0; - full_time->tm_min = 0; - full_time->tm_hour = 0; - t = mktime(full_time); - if (t == -1) - return; +void ro_gui_global_history_postinitialise(void) +{ - ro_gui_global_history_initialise_node(messages_get("DateToday"), t, 0); - if (weekday > 0) - ro_gui_global_history_initialise_node( - messages_get("DateYesterday"), t, -1); - for (i = 2; i <= weekday; i++) - ro_gui_global_history_initialise_node(NULL, t, -i); - ro_gui_global_history_initialise_node(messages_get("Date1Week"), - t, -weekday - 7); - ro_gui_global_history_initialise_node(messages_get("Date2Week"), - t, -weekday - 14); - ro_gui_global_history_initialise_node(messages_get("Date3Week"), - t, -weekday - 21); -} + /* Create our toolbar. */ -/** - * Create and initialise a node - */ -void ro_gui_global_history_initialise_node(const char *title, - time_t base, int days_back) -{ - struct tm *full_time; - char buffer[64]; - struct node *node; - - base += days_back * 60 * 60 * 24; - if (!title) { - full_time = localtime(&base); - strftime((char *)&buffer, (size_t)64, "%A", full_time); - node = tree_create_folder_node(NULL, buffer); - } else - node = tree_create_folder_node(NULL, title); - - if (!node) + global_history_window.toolbar = ro_gui_theme_create_toolbar(NULL, + THEME_HISTORY_TOOLBAR); + if (global_history_window.toolbar) + ro_gui_theme_attach_toolbar(global_history_window.toolbar, + global_history_window.window); + + /* Create the treeview with the window and toolbar. */ + + global_history_window.tv = + ro_treeview_create(global_history_window.window, + global_history_window.toolbar, + history_global_get_tree_flags()); + if (global_history_window.tv == NULL) { + LOG(("Failed to allocate treeview")); return; + } - node->retain_in_memory = true; - node->deleted = true; - node->editable = false; - global_history_base_node[global_history_base_node_count] = node; - global_history_base_node_time[global_history_base_node_count] = base; - global_history_base_node_count++; + /* Initialise the global history into the tree. */ + + history_global_initialise( + ro_treeview_get_tree(global_history_window.tv)); + + /* Build the global history window menu. */ + + static const struct ns_menu global_history_definition = { + "History", { + { "History", NO_ACTION, 0 }, + { "_History.Export", HISTORY_EXPORT, &dialog_saveas }, + { "History.Expand", TREE_EXPAND_ALL, 0 }, + { "History.Expand.All", TREE_EXPAND_ALL, 0 }, + { "History.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, + { "History.Expand.Links", TREE_EXPAND_LINKS, 0 }, + { "History.Collapse", TREE_COLLAPSE_ALL, 0 }, + { "History.Collapse.All", TREE_COLLAPSE_ALL, 0 }, + { "History.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, + { "History.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, + { "History.Toolbars", NO_ACTION, 0 }, + { "_History.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, + { "History.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 }, + { "Selection", TREE_SELECTION, 0 }, + { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, + { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, + { "SelectAll", TREE_SELECT_ALL, 0 }, + { "Clear", TREE_CLEAR_SELECTION, 0 }, + {NULL, 0, 0} + } + }; + global_history_window.menu = ro_gui_menu_define_menu( + &global_history_definition); + + ro_gui_wimp_event_register_window_menu(global_history_window.window, + global_history_window.menu, + ro_gui_global_history_menu_prepare, + ro_gui_global_history_menu_select, NULL, + ro_gui_global_history_menu_warning, false); } - /** - * Saves the global history's recent URL data. + * Open the global history window. */ -void ro_gui_global_history_save(void) + +void ro_gui_global_history_open(void) { - FILE *fp; - int i; - - /* save recent URLs */ - fp = fopen(option_recent_save, "w"); - if (!fp) - LOG(("Failed to open file '%s' for writing", - option_recent_save)); - else { - for (i = global_history_recent_count - 1; i >= 0; i--) - if (strlen(global_history_recent_url[i]) < - MAXIMUM_URL_LENGTH) - fprintf(fp, "%s\n", - global_history_recent_url[i]); - fclose(fp); + tree_set_redraw(ro_treeview_get_tree(global_history_window.tv), true); + + if (!ro_gui_dialog_open_top(global_history_window.window, + global_history_window.toolbar, 600, 800)) { + ro_treeview_set_origin(global_history_window.tv, 0, + -(ro_gui_theme_toolbar_height( + global_history_window.toolbar))); } } - /** - * Respond to a mouse click + * Prepare the global history menu for opening * - * \param pointer the pointer state - * \return true to indicate click handled + * \param window The window owning the menu. + * \param *menu The menu about to be opened. */ -bool ro_gui_global_history_click(wimp_pointer *pointer) + +void ro_gui_global_history_menu_prepare(wimp_w window, wimp_menu *menu) { - ro_gui_tree_click(pointer, global_history_tree); - if (pointer->buttons == wimp_CLICK_MENU) - ro_gui_menu_create(global_history_menu, pointer->pos.x, - pointer->pos.y, pointer->w); - else - ro_gui_menu_prepare_action(pointer->w, TREE_SELECTION, false); - return true; + bool selection; + + selection = ro_treeview_has_selection(global_history_window.tv); + + ro_gui_menu_set_entry_shaded(global_history_window.menu, + TREE_SELECTION, !selection); + ro_gui_menu_set_entry_shaded(global_history_window.menu, + TREE_CLEAR_SELECTION, !selection); + + ro_gui_menu_set_entry_shaded(global_history_window.menu, + TOOLBAR_BUTTONS, + (global_history_window.toolbar == NULL || + global_history_window.toolbar->editor)); + ro_gui_menu_set_entry_ticked(global_history_window.menu, + TOOLBAR_BUTTONS, + (global_history_window.toolbar != NULL && + (global_history_window.toolbar->display_buttons || + (global_history_window.toolbar->editor)))); + + ro_gui_menu_set_entry_shaded(global_history_window.menu, TOOLBAR_EDIT, + global_history_window.toolbar == NULL); + ro_gui_menu_set_entry_ticked(global_history_window.menu, TOOLBAR_EDIT, + (global_history_window.toolbar != NULL && + global_history_window.toolbar->editor)); + + ro_gui_save_prepare(GUI_SAVE_HISTORY_EXPORT_HTML, + NULL, NULL, NULL, NULL); } - /** - * Adds to the global history + * Handle submenu warnings for the global_hostory menu * - * \param url The URL to add + * \param window The window owning the menu. + * \param *menu The menu to which the warning applies. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. */ -void global_history_add(const char *url) -{ - const struct url_data *data; - data = urldb_get_url_data(url); - if (!data) - return; - - global_history_add_internal(url, data); +void ro_gui_global_history_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) +{ + /* Do nothing */ } /** - * Internal routine to actually perform global history addition + * Handle selections from the global history menu * - * \param url The URL to add - * \param data URL data associated with URL - * \return true (for urldb_iterate_entries) + * \param window The window owning the menu. + * \param *menu The menu from which the selection was made. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. + * \return true if action accepted; else false. */ -bool global_history_add_internal(const char *url, - const struct url_data *data) -{ - int i, j; - struct node *parent = NULL; - struct node *link; - struct node *node; - bool before = false; - int visit_date; - - assert(url && data); - - visit_date = data->last_visit; - - /* find parent node */ - for (i = 0; i < global_history_base_node_count; i++) { - if (global_history_base_node_time[i] <= visit_date) { - parent = global_history_base_node[i]; - break; - } - } - /* the entry is too old to care about */ - if (!parent) +bool ro_gui_global_history_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) +{ + switch (action) { + case HISTORY_EXPORT: + ro_gui_dialog_open_persistent(window, dialog_saveas, true); return true; - - if (parent->deleted) { - /* parent was deleted, so find place to insert it */ - link = global_history_tree->root; - - for (j = global_history_base_node_count - 1; j >= 0; j--) { - if (!global_history_base_node[j]->deleted && - global_history_base_node_time[j] > - global_history_base_node_time[i]) { - link = global_history_base_node[j]; - before = true; - break; - } - } - - tree_set_node_selected(global_history_tree, - parent, false); - tree_set_node_expanded(global_history_tree, - parent, false); - tree_link_node(link, parent, before); - - if (!global_history_init) { - tree_recalculate_node(global_history_tree, parent, true); - tree_recalculate_node_positions(global_history_tree, - global_history_tree->root); - tree_redraw_area(global_history_tree, - 0, 0, 16384, 16384); - } - } - - /* find any previous occurance */ - if (!global_history_init) { - node = ro_gui_global_history_find(url); - if (node) { - /* \todo: calculate old/new positions and redraw - * only the relevant portion */ - tree_redraw_area(global_history_tree, - 0, 0, 16384, 16384); - tree_update_URL_node(node, url, data); - tree_delink_node(node); - tree_link_node(parent, node, false); - tree_handle_node_changed(global_history_tree, - node, false, true); -/* ro_gui_tree_scroll_visible(hotlist_tree, - &node->data); -*/ return true; - } - } - - /* Add the node at the bottom */ - node = tree_create_URL_node_shared(parent, url, data); - if ((!global_history_init) && (node)) { - tree_redraw_area(global_history_tree, - node->box.x - NODE_INSTEP, - 0, NODE_INSTEP, 16384); - tree_handle_node_changed(global_history_tree, node, - true, false); + case TREE_EXPAND_ALL: + history_global_expand_all(); + return true; + case TREE_EXPAND_FOLDERS: + history_global_expand_directories(); + return true; + case TREE_EXPAND_LINKS: + history_global_expand_addresses(); + return true; + case TREE_COLLAPSE_ALL: + history_global_collapse_all(); + return true; + case TREE_COLLAPSE_FOLDERS: + history_global_collapse_directories(); + return true; + case TREE_COLLAPSE_LINKS: + history_global_collapse_addresses(); + return true; + case TREE_SELECTION_LAUNCH: + history_global_launch_selected(); + return true; + case TREE_SELECTION_DELETE: + history_global_delete_selected(); + return true; + case TREE_SELECT_ALL: + history_global_select_all(); + return true; + case TREE_CLEAR_SELECTION: + history_global_clear_selection(); + return true; + default: + return false; } - return true; + return false; } /** - * Find an entry in the global history - * - * \param url The URL to find - * \return Pointer to node, or NULL if not found + * Update the theme details of the global history window. */ -struct node *ro_gui_global_history_find(const char *url) + +void ro_gui_global_history_update_theme(void) { - int i; - struct node *node; - struct node_element *element; - - for (i = 0; i < global_history_base_node_count; i++) { - if (!global_history_base_node[i]->deleted) { - for (node = global_history_base_node[i]->child; - node; node = node->next) { - element = tree_find_element(node, - TREE_ELEMENT_URL); - if ((element) && !strcmp(url, element->text)) - return node; - } - } - } - return NULL; + ro_treeview_update_theme(global_history_window.tv); } - /** - * Adds an URL to the recently used list + * Check if a particular window handle is the global history window * - * \param url the URL to add (copied) + * \param window the window in question + * \return true if this window is the global history */ -void global_history_add_recent(const char *url) -{ - int i; - int j = -1; - char *current; - - /* try to find a string already there */ - for (i = 0; i < global_history_recent_count; i++) - if (global_history_recent_url[i] && - !strcmp(global_history_recent_url[i], url)) - j = i; - - /* already at head of list */ - if (j == 0) - return; - if (j < 0) { - /* add to head of list */ - free(global_history_recent_url[ - GLOBAL_HISTORY_RECENT_URLS - 1]); - memmove(&global_history_recent_url[1], - &global_history_recent_url[0], - (GLOBAL_HISTORY_RECENT_URLS - 1) * - sizeof(char *)); - global_history_recent_url[0] = strdup(url); - global_history_recent_count++; - if (global_history_recent_count > GLOBAL_HISTORY_RECENT_URLS) - global_history_recent_count = - GLOBAL_HISTORY_RECENT_URLS; - if (global_history_recent_count == 1) - ro_gui_window_prepare_navigate_all(); - } else { - /* move to head of list */ - current = global_history_recent_url[j]; - for (i = j; i > 0; i--) - global_history_recent_url[i] = - global_history_recent_url[i - 1]; - global_history_recent_url[0] = current; - } +bool ro_gui_global_history_check_window(wimp_w window) +{ +/* if (global_history_window.w == window) + return true; + else*/ + return false; } - /** - * Gets details of the currently used URL list. + * Check if a particular menu handle is the global history menu * - * \param count set to the current number of entries in the URL array on exit - * \return the current URL array + * \param *menu The menu in question. + * \return true if this menu is the global history menu */ -char **global_history_get_recent(int *count) + +bool ro_gui_global_history_check_menu(wimp_menu *menu) { - *count = global_history_recent_count; - return global_history_recent_url; + if (global_history_window.menu == menu) + return true; + else + return false; } + diff --git a/riscos/global_history.h b/riscos/global_history.h index 170cdaaca..951836d5d 100644 --- a/riscos/global_history.h +++ b/riscos/global_history.h @@ -1,5 +1,6 @@ /* * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -23,10 +24,13 @@ #ifndef _NETSURF_RISCOS_GLOBALHISTORY_H_ #define _NETSURF_RISCOS_GLOBALHISTORY_H_ -#define GLOBAL_HISTORY_RECENT_URLS 16 - -void ro_gui_global_history_initialise(void); +void ro_gui_global_history_preinitialise(void); +void ro_gui_global_history_postinitialise(void); +void ro_gui_global_history_open(void); void ro_gui_global_history_save(void); - +void ro_gui_global_history_update_theme(void); +bool ro_gui_global_history_check_window(wimp_w window); +bool ro_gui_global_history_check_menu(wimp_menu *menu); #endif + diff --git a/riscos/gui.c b/riscos/gui.c index 18a092c80..6968bb04f 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -55,20 +55,26 @@ #include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" +#include "desktop/cookies.h" #include "desktop/gui.h" +#include "desktop/history_global_core.h" +#include "desktop/hotlist.h" #include "desktop/netsurf.h" #include "desktop/options.h" #include "desktop/save_complete.h" #include "desktop/tree.h" +#include "desktop/tree_url_node.h" #include "render/box.h" #include "render/font.h" #include "render/html.h" #include "riscos/bitmap.h" #include "riscos/buffer.h" +#include "riscos/cookies.h" #include "riscos/dialog.h" #include "riscos/global_history.h" #include "riscos/gui.h" #include "riscos/help.h" +#include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/message.h" #include "riscos/options.h" @@ -78,6 +84,7 @@ #include "riscos/print.h" #include "riscos/query.h" #include "riscos/save.h" +#include "riscos/sslcert.h" #include "riscos/textselection.h" #include "riscos/theme.h" #include "riscos/treeview.h" @@ -184,7 +191,7 @@ static struct { } prev_sigs; /** Accepted wimp user messages. */ -static ns_wimp_message_list task_messages = { +static ns_wimp_message_list task_messages = { message_HELP_REQUEST, { message_DATA_SAVE, @@ -347,6 +354,8 @@ static void gui_init(int argc, char** argv) option_theme_path = strdup("NetSurf:Themes"); if (!option_theme_save) option_theme_save = strdup(CHOICES_PREFIX "Themes"); + if (!option_tree_icons_dir) + option_tree_icons_dir = strdup("NetSurf:Resources.Icons"); if (!option_theme || ! option_toolbar_browser || !option_toolbar_hotlist || !option_toolbar_history || @@ -355,7 +364,7 @@ static void gui_init(int argc, char** argv) !option_url_save || !option_hotlist_path || !option_hotlist_save || !option_recent_path || !option_recent_save || !option_theme_path || - !option_theme_save) + !option_theme_save || !option_tree_icons_dir) die("Failed initialising string options"); /* Create our choices directories */ @@ -394,7 +403,7 @@ static void gui_init(int argc, char** argv) default_stylesheet_url = strdup("file:///NetSurf:/Resources/CSS"); quirks_stylesheet_url = strdup("file:///NetSurf:/Resources/Quirks"); adblock_stylesheet_url = strdup("file:///NetSurf:/Resources/AdBlock"); - if (!default_stylesheet_url || !quirks_stylesheet_url || + if (!default_stylesheet_url || !quirks_stylesheet_url || !adblock_stylesheet_url) die("Failed initialising string constants."); @@ -479,9 +488,6 @@ static void gui_init(int argc, char** argv) /* Done with the templates file */ wimp_close_template(); - /* Initialise tree views (must be after UI sprites are loaded) */ - ro_gui_tree_initialise(); - /* Create Iconbar icon */ ro_gui_icon_bar_create(); @@ -670,6 +676,21 @@ static void gui_init2(int argc, char** argv) char *url = 0; bool open_window = option_open_browser_at_startup; + /* Complete initialisation of the treeview modules. */ + + /* certificate verification window */ + ro_gui_cert_postinitialise(); + + /* hotlist window */ + ro_gui_hotlist_postinitialise(); + + /* global history window */ + ro_gui_global_history_postinitialise(); + + /* cookies window */ + ro_gui_cookies_postinitialise(); + + /* parse command-line arguments */ if (argc == 2) { LOG(("parameters: '%s'", argv[1])); @@ -781,8 +802,9 @@ void gui_quit(void) urldb_save_cookies(option_cookie_jar); urldb_save(option_url_save); ro_gui_window_quit(); - ro_gui_global_history_save(); - ro_gui_hotlist_save(); + history_global_cleanup(); + cookies_cleanup(); + hotlist_cleanup(option_hotlist_save); ro_gui_saveas_quit(); rufl_quit(); free(gui_sprites); @@ -1109,6 +1131,9 @@ void ro_gui_null_reason_code(void) // break; default: + if (ro_gui_hotlist_check_window(gui_track_wimp_w)) + ro_treeview_mouse_at(gui_track_wimp_w, + &pointer); if (gui_track_wimp_w == history_window) ro_gui_history_mouse_at(&pointer); if (gui_track_wimp_w == dialog_url_complete) @@ -1203,8 +1228,10 @@ void ro_gui_pointer_entering_window(wimp_entering *entering) default: gui_track_wimp_w = entering->w; gui_track_gui_window = ro_gui_window_lookup(entering->w); - gui_track = gui_track_gui_window || gui_track_wimp_w == history_window || - gui_track_wimp_w == dialog_url_complete; + gui_track = gui_track_gui_window || + gui_track_wimp_w == history_window || + gui_track_wimp_w == dialog_url_complete || + ro_gui_hotlist_check_window(gui_track_wimp_w); break; } } @@ -1275,11 +1302,11 @@ void ro_gui_drag_end(wimp_dragged *drag) break; case GUI_DRAG_TREE_SELECT: - ro_gui_tree_selection_drag_end(drag); +// ro_gui_tree_selection_drag_end(drag); break; case GUI_DRAG_TREE_MOVE: - ro_gui_tree_move_drag_end(drag); +// ro_gui_tree_move_drag_end(drag); break; case GUI_DRAG_TOOLBAR_CONFIG: @@ -1485,12 +1512,7 @@ void ro_msg_dataload(wimp_message *message) char *url = 0; char *title = NULL; struct gui_window *g; - struct node *node; - struct node *link; os_error *error; - int x, y; - bool before; - const struct url_data *data; g = ro_gui_window_lookup(message->data.data_xfer.w); if (g) { @@ -1551,28 +1573,9 @@ void ro_msg_dataload(wimp_message *message) if (g) { browser_window_go(g->bw, url, 0, true); - } else if ((hotlist_tree) && ((wimp_w)hotlist_tree->handle == - message->data.data_xfer.w)) { - data = urldb_get_url_data(url); - if (!data) { - urldb_add_url(url); - urldb_set_url_persistence(url, true); - data = urldb_get_url_data(url); - } - if (data) { - 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, url, data, title); - 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); - if ((!title) && (!data->title)) - ro_gui_tree_start_edit(hotlist_tree, &node->data, NULL); - } +// } else if (ro_gui_hotlist_check_window(message->data.data_xfer.w)) { +// /* Drop URL into hotlist */ +// ro_gui_hotlist_url_drop(message, url); } else { browser_window_create(url, 0, 0, true, false); } diff --git a/riscos/gui.h b/riscos/gui.h index 101eedac2..c400494f8 100644 --- a/riscos/gui.h +++ b/riscos/gui.h @@ -64,7 +64,6 @@ extern bool gui_redraw_debug; extern osspriteop_area *gui_sprites; extern bool dialog_folder_add, dialog_entry_add, hotlist_insert; extern bool print_active, print_text_black; -extern struct tree *hotlist_tree, *global_history_tree, *cookies_tree; typedef enum { GUI_DRAG_NONE, GUI_DRAG_SELECTION, GUI_DRAG_DOWNLOAD_SAVE, GUI_DRAG_SAVE, GUI_DRAG_SCROLL, GUI_DRAG_STATUS_RESIZE, @@ -136,10 +135,6 @@ bool ro_gui_download_prequit(void); /* in 401login.c */ void ro_gui_401login_init(void); -/* in sslcert.c */ -void ro_gui_cert_init(void); -void ro_gui_cert_open(struct tree *tree, struct node *node); - /* in window.c */ void ro_gui_window_quit(void); /* void ro_gui_window_close_all(void); */ @@ -161,7 +156,10 @@ void ro_gui_window_process_reformats(void); void ro_gui_window_default_options(struct browser_window *bw); void ro_gui_window_redraw_all(void); void ro_gui_window_prepare_navigate_all(void); -browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons); +browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons, + wimp_icon_flags type); +browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons, + wimp_icon_flags type); bool ro_gui_shift_pressed(void); bool ro_gui_ctrl_pressed(void); bool ro_gui_alt_pressed(void); @@ -178,13 +176,6 @@ void ro_gui_history_open(struct browser_window *bw, struct history *history, bool pointer); void ro_gui_history_mouse_at(wimp_pointer *pointer); -/* in hotlist.c */ -void ro_gui_hotlist_initialise(void); -void ro_gui_hotlist_save(void); -void ro_gui_hotlist_prepare_folder_dialog(struct node *node); -void ro_gui_hotlist_prepare_entry_dialog(struct node *node); -bool ro_gui_hotlist_dialog_apply(wimp_w w); - /* in filetype.c */ int ro_content_filetype(struct hlcache_handle *c); int ro_content_filetype_from_type(content_type type); diff --git a/riscos/help.c b/riscos/help.c index 6d5efe974..968fcb7c4 100644 --- a/riscos/help.c +++ b/riscos/help.c @@ -28,7 +28,10 @@ #include "oslib/taskmanager.h" #include "oslib/wimp.h" #include "desktop/tree.h" +#include "riscos/cookies.h" +#include "riscos/global_history.h" #include "riscos/gui.h" +#include "riscos/hotlist.h" #include "riscos/help.h" #include "riscos/menus.h" #include "riscos/options.h" @@ -100,7 +103,7 @@ void ro_gui_interactive_help_request(wimp_message *message) os_error *error; const char *auto_text; int i; - + /* check we aren't turned off */ if (!option_interactive_help) return; @@ -124,18 +127,17 @@ void ro_gui_interactive_help_request(wimp_message *message) sprintf(message_token, "%s%i", auto_text, (int)icon); else if (window == wimp_ICON_BAR) sprintf(message_token, "HelpIconbar"); - else if ((hotlist_tree) && (window == (wimp_w)hotlist_tree->handle)) { - i = ro_gui_tree_help(message_data->pos.x, message_data->pos.y); + else if (ro_gui_hotlist_check_window(message->data.data_xfer.w)) { + i = ro_treeview_get_help(message_data); sprintf(message_token, (i >= 0) ? "HelpTree%i" :"HelpHotlist%i", i); - } else if ((global_history_tree) && - (window == (wimp_w)global_history_tree->handle)) { - i = ro_gui_tree_help(message_data->pos.x, message_data->pos.y); + } else if (ro_gui_global_history_check_window( + message->data.data_xfer.w)) { + i = ro_treeview_get_help(message_data); sprintf(message_token, (i >= 0) ? "HelpTree%i" :"HelpGHistory%i", i); - } else if ((cookies_tree) && - (window == (wimp_w)cookies_tree->handle)) { - i = ro_gui_tree_help(message_data->pos.x, message_data->pos.y); + } else if (ro_gui_cookies_check_window(message->data.data_xfer.w)) { + i = ro_treeview_get_help(message_data); sprintf(message_token, (i >= 0) ? "HelpTree%i" :"HelpCookies%i", i); } else if (ro_gui_window_lookup(window) != NULL) @@ -173,11 +175,11 @@ void ro_gui_interactive_help_request(wimp_message *message) sprintf(message_token, "HelpIconMenu"); else if (current_menu == browser_menu) sprintf(message_token, "HelpBrowserMenu"); - else if (current_menu == hotlist_menu) + else if (ro_gui_hotlist_check_menu(current_menu)) sprintf(message_token, "HelpHotlistMenu"); - else if (current_menu == global_history_menu) + else if (ro_gui_global_history_check_menu(current_menu)) sprintf(message_token, "HelpGHistoryMenu"); - else if (current_menu == cookies_menu) + else if (ro_gui_cookies_check_menu(current_menu)) sprintf(message_token, "HelpCookiesMenu"); else return; @@ -323,7 +325,7 @@ void ro_gui_interactive_help_start(void) char *help_start; wimp_t task = 0; os_error *error; - + /* don't launch a second copy of anything */ if (ro_gui_interactive_help_available()) return; diff --git a/riscos/hotlist.c b/riscos/hotlist.c index dd611b691..6e2d176cc 100644 --- a/riscos/hotlist.c +++ b/riscos/hotlist.c @@ -1,5 +1,6 @@ /* * Copyright 2004, 2005 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -31,10 +32,13 @@ #include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" +#include "desktop/hotlist.h" #include "desktop/tree.h" #include "riscos/dialog.h" +#include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/options.h" +#include "riscos/save.h" #include "riscos/theme.h" #include "riscos/treeview.h" #include "riscos/wimp.h" @@ -44,350 +48,300 @@ #include "utils/utils.h" #include "utils/url.h" +static void ro_gui_hotlist_menu_prepare(wimp_w window, wimp_menu *menu); +static bool ro_gui_hotlist_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); +static void ro_gui_hotlist_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action); -static void ro_gui_hotlist_visited(hlcache_handle *c, struct tree *tree, - struct node *node); -static bool ro_gui_hotlist_click(wimp_pointer *pointer); - - -/* The hotlist window, toolbar and plot origins -*/ -static wimp_w hotlist_window; -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. -*/ -struct node *dialog_folder_node; -struct node *dialog_entry_node; - -static const struct { - const char *url; - const char *msg_key; -} default_entries[] = { - { "http://www.netsurf-browser.org/", "HotlistHomepage" }, - { "http://www.netsurf-browser.org/downloads/riscos/testbuilds", "HotlistTestBuild" }, - { "http://www.netsurf-browser.org/documentation", "HotlistDocumentation" }, - { "http://sourceforge.net/tracker/?atid=464312&group_id=51719", - "HotlistBugTracker" }, - { "http://sourceforge.net/tracker/?atid=464315&group_id=51719", - "HotlistFeatureRequest" } -}; -#define ENTRIES_COUNT (sizeof(default_entries) / sizeof(default_entries[0])) - -void ro_gui_hotlist_initialise(void) +/* The RISC OS hotlist window, toolbar and treeview data. */ + +static struct ro_hotlist { + wimp_w window; /*< The hotlist RO window handle. */ + struct toolbar *toolbar; /*< The hotlist toolbar handle. */ + ro_treeview *tv; /*< The hotlist treeview handle. */ + wimp_menu *menu; /*< The hotlist window menu. */ +} hotlist_window; + +/** + * Pre-Initialise the hotlist tree. This is called for things that need to + * be done at the gui_init() stage, such as loading templates. + */ + +void ro_gui_hotlist_preinitialise(void) { - FILE *fp; - struct node *node; - const struct url_data *data; + /* Create our window. */ - /* create our window */ - hotlist_window = ro_gui_dialog_create("tree"); - ro_gui_set_window_title(hotlist_window, + hotlist_window.window = ro_gui_dialog_create("tree"); + ro_gui_set_window_title(hotlist_window.window, messages_get("Hotlist")); - ro_gui_wimp_event_register_redraw_window(hotlist_window, - ro_gui_tree_redraw); - ro_gui_wimp_event_register_open_window(hotlist_window, - ro_gui_tree_open); - ro_gui_wimp_event_register_mouse_click(hotlist_window, - ro_gui_hotlist_click); - - /* Either load or create a hotlist - */ - fp = fopen(option_hotlist_path, "r"); - if (!fp) { - int i; - - 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; - return; - } - hotlist_tree->root->expanded = true; - node = tree_create_folder_node(hotlist_tree->root, "NetSurf"); - if (!node) - node = hotlist_tree->root; - - for (i = 0; i != ENTRIES_COUNT; i++) { - data = urldb_get_url_data(default_entries[i].url); - if (!data) { - urldb_add_url(default_entries[i].url); - urldb_set_url_persistence( - default_entries[i].url, - true); - data = urldb_get_url_data( - default_entries[i].url); - } - if (data) { - tree_create_URL_node(node, - default_entries[i].url, data, - messages_get(default_entries[i].msg_key)); - } - } - tree_initialise(hotlist_tree); - } else { - fclose(fp); - hotlist_tree = options_load_tree(option_hotlist_path); - } - if (!hotlist_tree) return; - hotlist_tree->handle = (int)hotlist_window; - hotlist_tree->movable = true; - ro_gui_wimp_event_set_user_data(hotlist_window, hotlist_tree); - ro_gui_wimp_event_register_keypress(hotlist_window, - ro_gui_tree_keypress); - - /* Create our toolbar - */ - hotlist_tree->toolbar = ro_gui_theme_create_toolbar(NULL, - THEME_HOTLIST_TOOLBAR); - if (hotlist_tree->toolbar) - ro_gui_theme_attach_toolbar(hotlist_tree->toolbar, - hotlist_window); } - /** - * Perform a save to the default file + * Initialise the hotlist tree, at the gui_init2() stage. */ -void ro_gui_hotlist_save(void) + +void ro_gui_hotlist_postinitialise(void) { - os_error *error; + /* Create our toolbar. */ - if (!hotlist_tree) + hotlist_window.toolbar = ro_gui_theme_create_toolbar(NULL, + THEME_HOTLIST_TOOLBAR); + if (hotlist_window.toolbar) + ro_gui_theme_attach_toolbar(hotlist_window.toolbar, + hotlist_window.window); + + /* Create the treeview with the window and toolbar. */ + + hotlist_window.tv = ro_treeview_create(hotlist_window.window, + hotlist_window.toolbar, hotlist_get_tree_flags()); + if (hotlist_window.tv == NULL) { + LOG(("Failed to allocate treeview")); return; + } - /* Save to our file - */ - options_save_tree(hotlist_tree, option_hotlist_save, - "NetSurf hotlist"); - error = xosfile_set_type(option_hotlist_save, 0xfaf); - if (error) - LOG(("xosfile_set_type: 0x%x: %s", - error->errnum, error->errmess)); + /* Initialise the hotlist into the tree. */ + + hotlist_initialise(ro_treeview_get_tree(hotlist_window.tv), + option_hotlist_path); + + + /* Build the hotlist window menu. */ + + static const struct ns_menu hotlist_definition = { + "Hotlist", { + { "Hotlist", NO_ACTION, 0 }, + { "Hotlist.New", NO_ACTION, 0 }, + { "Hotlist.New.Folder", TREE_NEW_FOLDER, 0 }, + { "Hotlist.New.Link", TREE_NEW_LINK, 0 }, + { "_Hotlist.Export", HOTLIST_EXPORT, &dialog_saveas }, + { "Hotlist.Expand", TREE_EXPAND_ALL, 0 }, + { "Hotlist.Expand.All", TREE_EXPAND_ALL, 0 }, + { "Hotlist.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, + { "Hotlist.Expand.Links", TREE_EXPAND_LINKS, 0 }, + { "Hotlist.Collapse", TREE_COLLAPSE_ALL, 0 }, + { "Hotlist.Collapse.All", TREE_COLLAPSE_ALL, 0 }, + { "Hotlist.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, + { "Hotlist.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, + { "Hotlist.Toolbars", NO_ACTION, 0 }, + { "_Hotlist.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, + { "Hotlist.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 }, + { "Selection", TREE_SELECTION, 0 }, + { "Selection.Edit", TREE_SELECTION_EDIT, 0 }, + { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, + { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, + { "SelectAll", TREE_SELECT_ALL, 0 }, + { "Clear", TREE_CLEAR_SELECTION, 0 }, + {NULL, 0, 0} + } + }; + + hotlist_window.menu = ro_gui_menu_define_menu(&hotlist_definition); + + ro_gui_wimp_event_register_window_menu(hotlist_window.window, + hotlist_window.menu, ro_gui_hotlist_menu_prepare, + ro_gui_hotlist_menu_select, NULL, + ro_gui_hotlist_menu_warning, false); } /** - * Respond to a mouse click + * Open the hotlist window. * - * \param pointer the pointer state */ -bool ro_gui_hotlist_click(wimp_pointer *pointer) + +void ro_gui_hotlist_open(void) { - ro_gui_tree_click(pointer, hotlist_tree); - if (pointer->buttons == wimp_CLICK_MENU) - ro_gui_menu_create(hotlist_menu, pointer->pos.x, - pointer->pos.y, pointer->w); - else - ro_gui_menu_prepare_action(pointer->w, TREE_SELECTION, false); - return true; + tree_set_redraw(ro_treeview_get_tree(hotlist_window.tv), true); + + if (!ro_gui_dialog_open_top(hotlist_window.window, + hotlist_window.toolbar, 600, 800)) { + + xwimp_set_caret_position(hotlist_window.window, -1, -100, -100, 32, -1); +// \todo ro_gui_theme_process_toolbar(hotlist_window.toolbar, -1); + ro_treeview_set_origin(hotlist_window.tv, 0, + -(ro_gui_theme_toolbar_height( + hotlist_window.toolbar))); +// ro_gui_tree_stop_edit(tree); +// +// if (tree->root->child) { +// tree_set_node_selected(tree, tree->root, false); +// tree_handle_node_changed(tree, tree->root, +// false, true); +// } + } } - /** - * Informs the hotlist that some content has been visited + * Prepare the hotlist menu for opening * - * \param content the content visited + * \param window The window owning the menu. + * \param *menu The menu about to be opened. */ -void hotlist_visited(hlcache_handle *c) + +void ro_gui_hotlist_menu_prepare(wimp_w window, wimp_menu *menu) { - if ((!c) || (!content_get_url(c)) || (!hotlist_tree)) - return; - ro_gui_hotlist_visited(c, hotlist_tree, hotlist_tree->root); + bool selection; + + selection = ro_treeview_has_selection(hotlist_window.tv); + + ro_gui_menu_set_entry_shaded(hotlist_window.menu, TREE_SELECTION, + !selection); + ro_gui_menu_set_entry_shaded(hotlist_window.menu, TREE_CLEAR_SELECTION, + !selection); + + ro_gui_menu_set_entry_shaded(hotlist_window.menu, TOOLBAR_BUTTONS, + (hotlist_window.toolbar == NULL || + hotlist_window.toolbar->editor)); + ro_gui_menu_set_entry_ticked(hotlist_window.menu, TOOLBAR_BUTTONS, + (hotlist_window.toolbar != NULL && + (hotlist_window.toolbar->display_buttons || + hotlist_window.toolbar->editor))); + + ro_gui_menu_set_entry_shaded(hotlist_window.menu, TOOLBAR_EDIT, + hotlist_window.toolbar == NULL); + ro_gui_menu_set_entry_ticked(hotlist_window.menu, TOOLBAR_EDIT, + (hotlist_window.toolbar != NULL && + hotlist_window.toolbar->editor)); + + ro_gui_save_prepare(GUI_SAVE_HOTLIST_EXPORT_HTML, + NULL, NULL, NULL, NULL); } - /** - * Informs the hotlist that some content has been visited + * Handle submenu warnings for the hotlist menu * - * \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 + * \param window The window owning the menu. + * \param *menu The menu to which the warning applies. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. */ -void ro_gui_hotlist_visited(hlcache_handle *c, struct tree *tree, - struct node *node) + +void ro_gui_hotlist_menu_warning(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) { - 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_get_url(c)))) { - tree_update_URL_node(node, content_get_url(c), - NULL); - tree_handle_node_changed(tree, node, true, - false); - } - } - if (node->child) - ro_gui_hotlist_visited(c, tree, node->child); - } + /* Do nothing */ } - /** - * Prepares the folder dialog contents for a node + * Handle selections from the hotlist menu * - * \param node the node to prepare the dialogue for, or NULL + * \param window The window owning the menu. + * \param *menu The menu from which the selection was made. + * \param *selection The wimp menu selection data. + * \param action The selected menu action. + * \return true if action accepted; else false. */ -void ro_gui_hotlist_prepare_folder_dialog(struct node *node) + +bool ro_gui_hotlist_menu_select(wimp_w window, wimp_menu *menu, + wimp_selection *selection, menu_action action) { - const char *name; - const char *title; - - dialog_folder_node = node; - if (node) { - title = messages_get("EditFolder"); - name = node->data.text; - } else { - title = messages_get("NewFolder"); - name = messages_get("Folder"); + switch (action) { + case HOTLIST_EXPORT: + ro_gui_dialog_open_persistent(window, dialog_saveas, true); + return true; + case TREE_NEW_FOLDER: + hotlist_add_folder(); + return true; + case TREE_NEW_LINK: + hotlist_add_entry(); + return true; + case TREE_EXPAND_ALL: + hotlist_expand_all(); + return true; + case TREE_EXPAND_FOLDERS: + hotlist_expand_directories(); + return true; + case TREE_EXPAND_LINKS: + hotlist_expand_addresses(); + return true; + case TREE_COLLAPSE_ALL: + hotlist_collapse_all(); + return true; + case TREE_COLLAPSE_FOLDERS: + hotlist_collapse_directories(); + return true; + case TREE_COLLAPSE_LINKS: + hotlist_collapse_addresses(); + return true; + case TREE_SELECTION_EDIT: + hotlist_edit_selected(); + return true; + case TREE_SELECTION_LAUNCH: + hotlist_launch_selected(); + return true; + case TREE_SELECTION_DELETE: + hotlist_delete_selected(); + return true; + case TREE_SELECT_ALL: + hotlist_select_all(); + return true; + case TREE_CLEAR_SELECTION: + hotlist_clear_selection(); + return true; + default: + return false; } - ro_gui_set_window_title(dialog_folder, title); - ro_gui_set_icon_string(dialog_folder, ICON_FOLDER_NAME, name, true); - ro_gui_wimp_event_memorise(dialog_folder); -} + return false; +} /** - * Prepares the entry dialog contents for a node - * - * \param node the node to prepare the dialogue for, or NULL + * Update the theme details of the hotlist window. */ -void ro_gui_hotlist_prepare_entry_dialog(struct node *node) + +void ro_gui_hotlist_update_theme(void) { - struct node_element *element; - const char *name; - const char *title; - const char *url = ""; - - dialog_entry_node = node; - if (node) { - title = messages_get("EditLink"); - name = node->data.text; - if ((element = tree_find_element(node, TREE_ELEMENT_URL))) - url = element->text; - } else { - title = messages_get("NewLink"); - name = messages_get("Link"); - } - ro_gui_set_window_title(dialog_entry, title); - ro_gui_set_icon_string(dialog_entry, ICON_ENTRY_NAME, name, true); - ro_gui_set_icon_string(dialog_entry, ICON_ENTRY_URL, url, true); - ro_gui_wimp_event_memorise(dialog_entry); + ro_treeview_update_theme(hotlist_window.tv); } - /** - * Apply the settings of dialog window (folder/entry edit) + * Check if a particular window handle is the hotlist window * - * \param w the window to apply + * \param window The window in question + * \return true if this window is the hotlist */ -bool ro_gui_hotlist_dialog_apply(wimp_w w) +bool ro_gui_hotlist_check_window(wimp_w window) { - struct node_element *element; - struct node *node; - const char *icon; - char *title; - char *url = NULL; - url_func_result res; - const struct url_data *data; - - /* get our data */ - if (w == dialog_entry) { - icon = ro_gui_get_icon_string(w, ICON_ENTRY_URL); - - res = url_normalize(icon, &url); - if (res != URL_FUNC_OK) { - warn_user(res == URL_FUNC_FAILED ? "NoURLError" - : "NoMemory", 0); - return false; - } + if (hotlist_window.window == window) + return true; + else + return false; +} - icon = ro_gui_get_icon_string(w, ICON_ENTRY_NAME); - while (isspace(*icon)) - icon++; - title = strdup(icon); +/** + * Check if a particular menu handle is the hotlist menu + * + * \param *menu The menu in question. + * \return true if this menu is the hotlist menu + */ - node = dialog_entry_node; - } else { - icon = ro_gui_get_icon_string(w, ICON_FOLDER_NAME); - while (isspace(*icon)) - icon++; - title = strdup(icon); +bool ro_gui_hotlist_check_menu(wimp_menu *menu) +{ + if (hotlist_window.menu == menu) + return true; + else + return false; +} - node = dialog_folder_node; - } +#if 0 +/** + * Handle URL dropped on hotlist + * + * \param message the wimp message we're acting on + * \param url the URL to add + */ +void ro_gui_hotlist_url_drop(wimp_message *message, const char *url) +{ + int x, y; + if (hotlist_window.window != message->data.data_xfer.w) + return; - if (title != NULL) - strip(title); - - /* check for lack of text */ - if (title == NULL || title[0] == '\0') { - if (title == NULL) - warn_user("NoMemory", 0); - else if (title[0] == '\0') - warn_user("NoNameError", 0); - free(url); - free(title); - node = NULL; - return false; - } - ro_gui_set_icon_string(w, url ? ICON_ENTRY_NAME : ICON_FOLDER_NAME, - title, false); - - /* update/insert our data */ - if (!node) { - if (url) { - data = urldb_get_url_data(url); - if (!data) { - urldb_add_url(url); - urldb_set_url_persistence(url, true); - data = urldb_get_url_data(url); - } - if (!data) { - free(url); - free(title); - return false; - } - if (!data->title) - urldb_set_url_title(url, title); - node = dialog_entry_node = tree_create_URL_node( - hotlist_tree->root, url, data, title); - - } else { - node = dialog_folder_node = tree_create_folder_node( - hotlist_tree->root, title); - } - free(url); - free(title); - if (!node) { - warn_user("NoMemory", 0); - return false; - } - 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 { - element = tree_find_element(node, TREE_ELEMENT_URL); - if (element) { - free((void *)element->text); - element->text = url; - ro_gui_set_icon_string(w, ICON_ENTRY_URL, url, true); - } - free((void *)node->data.text); - node->data.text = title; - tree_handle_node_changed(hotlist_tree, node, true, false); - } - return true; + ro_gui_tree_get_tree_coordinates(hotlist_window.tree, + message->data.data_xfer.pos.x, + message->data.data_xfer.pos.y, + &x, &y); + hotlist_add_page_xy(url, x, y); } +#endif + diff --git a/beos/beos_history.h b/riscos/hotlist.h index 71536f1ca..e416b2124 100644 --- a/beos/beos_history.h +++ b/riscos/hotlist.h @@ -1,6 +1,6 @@ /* - * Copyright 2008 François Revol <mmu_man@users.sourceforge.net> - * Copyright 2006 Rob Kendrick <rjek@rjek.com> + * Copyright 2006 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -17,20 +17,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef __NSBEOS_HISTORY_H__ -#define __NSBEOS_HISTORY_H__ +/** \file + * Hotlist (interface). + */ -#include <View.h> -#include <Window.h> +#ifndef _NETSURF_RISCOS_HOTLIST_H_ +#define _NETSURF_RISCOS_HOTLIST_H_ -extern BWindow *wndHistory; +void ro_gui_hotlist_preinitialise(void); +void ro_gui_hotlist_postinitialise(void); +void ro_gui_hotlist_open(void); +void ro_gui_hotlist_save(void); +void ro_gui_hotlist_update_theme(void); +bool ro_gui_hotlist_check_window(wimp_w window); +bool ro_gui_hotlist_check_menu(wimp_menu *menu); -void nsbeos_history_init(void); -void nsbeos_history_update(void); -#warning XXX -#if 0 /* GTK */ -void nsbeos_history_row_activated(GtkTreeView *, GtkTreePath *, - GtkTreeViewColumn *, gpointer); #endif -#endif /* __NSGTK_HISTORY_H__ */ diff --git a/riscos/menus.c b/riscos/menus.c index da0a686ad..39e1a4c03 100644 --- a/riscos/menus.c +++ b/riscos/menus.c @@ -34,9 +34,12 @@ #include "content/content.h" #include "content/hlcache.h" #include "content/urldb.h" +#include "desktop/cookies.h" #include "desktop/browser.h" #include "desktop/gui.h" +#include "desktop/history_global_core.h" #include "desktop/history_core.h" +#include "desktop/hotlist.h" #include "desktop/netsurf.h" #include "desktop/selection.h" #include "desktop/textinput.h" @@ -49,12 +52,14 @@ #include "riscos/gui.h" #include "riscos/global_history.h" #include "riscos/help.h" +#include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/options.h" #include "riscos/save.h" #include "riscos/tinct.h" #include "riscos/theme.h" #include "riscos/treeview.h" +#include "riscos/url_suggest.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "utils/log.h" @@ -63,18 +68,6 @@ #include "utils/utils.h" #include "utils/utf8.h" - -struct ns_menu_entry { - const char *text; /**< menu text (from messages) */ - menu_action action; /**< associated action */ - wimp_w *sub_window; /**< sub-window if any */ -}; - -struct ns_menu { - const char *title; - struct ns_menu_entry entries[]; -}; - struct menu_definition_entry { menu_action action; /**< menu action */ wimp_menu_entry *menu_entry; /**< corresponding menu entry */ @@ -91,7 +84,6 @@ struct menu_definition { }; -static wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu); static void ro_gui_menu_define_menu_add(struct menu_definition *definition, const struct ns_menu *menu, int depth, wimp_menu_entry *parent_entry, @@ -101,15 +93,11 @@ static struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu, menu_action action); static menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry); -static void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action, - bool shaded); -static void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action, - bool ticked); static void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g, struct browser_window **bw, hlcache_handle **h, - struct toolbar **toolbar, struct tree **tree); + struct toolbar **toolbar, + bool *is_cookies, bool *is_hotlist, bool *is_global_history); static int ro_gui_menu_get_checksum(void); -static bool ro_gui_menu_prepare_url_suggest(void); static void ro_gui_menu_prepare_pageinfo(struct gui_window *g); static void ro_gui_menu_prepare_objectinfo(hlcache_handle *object, const char *href); @@ -143,12 +131,12 @@ static wimp_i current_menu_icon; /** The height of the iconbar menu */ int iconbar_menu_height = 5 * 44; /** The available menus */ -wimp_menu *iconbar_menu, *browser_menu, *hotlist_menu, *global_history_menu, *cookies_menu, - *image_quality_menu, *browser_toolbar_menu, - *tree_toolbar_menu, *proxy_type_menu, *languages_menu; -/** URL suggestion menu */ -static wimp_MENU(GLOBAL_HISTORY_RECENT_URLS) url_suggest; -wimp_menu *url_suggest_menu = (wimp_menu *)&url_suggest; +wimp_menu *iconbar_menu, *browser_menu, *image_quality_menu, + *browser_toolbar_menu, *tree_toolbar_menu, + *proxy_type_menu, *languages_menu; + +static wimp_MENU(URL_SUGGEST_MAX_URLS) url_suggest_menu_block; +wimp_menu *url_suggest_menu = (wimp_menu *) &url_suggest_menu_block; /* the values given in PRM 3-157 for how to check menus/windows are * incorrect so we use a hack of checking if the sub-menu has bit 0 @@ -277,94 +265,6 @@ void ro_gui_menu_init(void) }; browser_menu = ro_gui_menu_define_menu(&browser_definition); - /* hotlist menu */ - static wimp_w one = (wimp_w) 1; - static const struct ns_menu hotlist_definition = { - "Hotlist", { - { "Hotlist", NO_ACTION, 0 }, - { "Hotlist.New", NO_ACTION, 0 }, - { "Hotlist.New.Folder", TREE_NEW_FOLDER, &dialog_folder }, - { "Hotlist.New.Link", TREE_NEW_LINK, &dialog_entry }, - { "_Hotlist.Export", HOTLIST_EXPORT, &dialog_saveas }, - { "Hotlist.Expand", TREE_EXPAND_ALL, 0 }, - { "Hotlist.Expand.All", TREE_EXPAND_ALL, 0 }, - { "Hotlist.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, - { "Hotlist.Expand.Links", TREE_EXPAND_LINKS, 0 }, - { "Hotlist.Collapse", TREE_COLLAPSE_ALL, 0 }, - { "Hotlist.Collapse.All", TREE_COLLAPSE_ALL, 0 }, - { "Hotlist.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, - { "Hotlist.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, - { "Hotlist.Toolbars", NO_ACTION, 0 }, - { "_Hotlist.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, - { "Hotlist.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 }, - { "Selection", TREE_SELECTION, 0 }, - /* We want a window, but it changes depending upon - * context. Therefore, ensure that the structure is - * created so that we can dynamically modify the - * actual item presented. We do this by creating a - * fake wimp_w with the value 1, which indicates a - * window handle as opposed to a submenu. */ - { "Selection.Edit", TREE_SELECTION_EDIT, &one }, - { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, - { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, - { "SelectAll", TREE_SELECT_ALL, 0 }, - { "Clear", TREE_CLEAR_SELECTION, 0 }, - {NULL, 0, 0} - } - }; - hotlist_menu = ro_gui_menu_define_menu(&hotlist_definition); - - /* history menu */ - static const struct ns_menu global_history_definition = { - "History", { - { "History", NO_ACTION, 0 }, - { "_History.Export", HISTORY_EXPORT, &dialog_saveas }, - { "History.Expand", TREE_EXPAND_ALL, 0 }, - { "History.Expand.All", TREE_EXPAND_ALL, 0 }, - { "History.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, - { "History.Expand.Links", TREE_EXPAND_LINKS, 0 }, - { "History.Collapse", TREE_COLLAPSE_ALL, 0 }, - { "History.Collapse.All", TREE_COLLAPSE_ALL, 0 }, - { "History.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, - { "History.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, - { "History.Toolbars", NO_ACTION, 0 }, - { "_History.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, - { "History.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 }, - { "Selection", TREE_SELECTION, 0 }, - { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, - { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, - { "SelectAll", TREE_SELECT_ALL, 0 }, - { "Clear", TREE_CLEAR_SELECTION, 0 }, - {NULL, 0, 0} - } - }; - global_history_menu = ro_gui_menu_define_menu( - &global_history_definition); - - /* history menu */ - static const struct ns_menu cookies_definition = { - "Cookies", { - { "Cookies", NO_ACTION, 0 }, - { "Cookies.Expand", TREE_EXPAND_ALL, 0 }, - { "Cookies.Expand.All", TREE_EXPAND_ALL, 0 }, - { "Cookies.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, - { "Cookies.Expand.Links", TREE_EXPAND_LINKS, 0 }, - { "Cookies.Collapse", TREE_COLLAPSE_ALL, 0 }, - { "Cookies.Collapse.All", TREE_COLLAPSE_ALL, 0 }, - { "Cookies.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, - { "Cookies.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, - { "Cookies.Toolbars", NO_ACTION, 0 }, - { "_Cookies.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, - { "Cookies.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 }, - { "Selection", TREE_SELECTION, 0 }, - { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, - { "SelectAll", TREE_SELECT_ALL, 0 }, - { "Clear", TREE_CLEAR_SELECTION, 0 }, - {NULL, 0, 0} - } - }; - cookies_menu = ro_gui_menu_define_menu(&cookies_definition); - /* image quality menu */ static const struct ns_menu images_definition = { "Display", { @@ -415,9 +315,7 @@ void ro_gui_menu_init(void) proxy_type_menu = ro_gui_menu_define_menu(&proxy_type_definition); /* special case menus */ - url_suggest_menu->title_data.indirected_text.text = - (char *) messages_get("URLSuggest"); - ro_gui_menu_init_structure(url_suggest_menu, GLOBAL_HISTORY_RECENT_URLS); + ro_gui_url_suggest_init(url_suggest_menu); /* Note: This table *must* be kept in sync with the LangNames file */ static const struct ns_menu lang_definition = { @@ -540,7 +438,7 @@ void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w) /* prepare the menu state */ if (menu == url_suggest_menu) { - if (!ro_gui_menu_prepare_url_suggest()) + if (!ro_gui_url_suggest_prepare_menu()) return; } else if (menu == recent_search_menu) { if (!ro_gui_search_prepare_menu()) @@ -618,8 +516,8 @@ void ro_gui_menu_closed(bool cleanup) struct browser_window *bw; hlcache_handle *h; struct toolbar *t; - struct tree *tree; os_error *error; + bool is_cookies, is_hotlist, is_global_history; if (current_menu) { @@ -630,15 +528,16 @@ void ro_gui_menu_closed(bool cleanup) warn_user("MenuError", error->errmess); } ro_gui_menu_get_window_details(current_menu_window, - &g, &bw, &h, &t, &tree); - current_menu = NULL; + &g, &bw, &h, &t, + &is_cookies, &is_hotlist, + &is_global_history); if (cleanup) { - ro_gui_wimp_event_menus_closed(); - - if (tree) - ro_gui_tree_menu_closed(tree); + ro_gui_wimp_event_menus_closed(current_menu_window, + current_menu_icon, current_menu); } + + current_menu = NULL; } current_menu_window = NULL; @@ -668,15 +567,16 @@ void ro_gui_menu_objects_moved(void) */ void ro_gui_menu_selection(wimp_selection *selection) { - int i, j; - wimp_menu_entry *menu_entry; - menu_action action; - wimp_pointer pointer; - struct gui_window *g = NULL; - wimp_menu *menu; - os_error *error; - int previous_menu_icon = current_menu_icon; - char *url; + int i, j; + bool needs_prepare; + wimp_menu_entry *menu_entry; + menu_action action; + wimp_pointer pointer; + struct gui_window *g = NULL; + wimp_menu *menu; + os_error *error; + int previous_menu_icon = current_menu_icon; + char *url; /* if we are using gui_multitask then menu selection events * may be delivered after the menu has been closed. As such, @@ -686,41 +586,58 @@ void ro_gui_menu_selection(wimp_selection *selection) assert(current_menu_window); - /* get the menu entry and associated action */ + /* get the menu entry and associated action and definition */ menu_entry = ¤t_menu->entries[selection->items[0]]; for (i = 1; selection->items[i] != -1; i++) menu_entry = &menu_entry->sub_menu-> entries[selection->items[i]]; action = ro_gui_menu_find_action(current_menu, menu_entry); - /* perform menu action */ - if (action != NO_ACTION) - ro_gui_menu_handle_action(current_menu_window, action, false); - - /* perform non-automated actions */ - if (current_menu == url_suggest_menu) { - g = ro_gui_toolbar_lookup(current_menu_window); - if (g) { - url = url_suggest_menu->entries[selection->items[0]]. - data.indirected_text.text; - gui_window_set_url(g, url); - browser_window_go(g->bw, url, 0, true); - global_history_add_recent(url); - } - } else if (current_menu == gui_form_select_menu) { - g = ro_gui_window_lookup(current_menu_window); - assert(g); - - if (selection->items[0] >= 0) { - form_select_process_selection(g->bw->current_content, - gui_form_select_control, - selection->items[0]); + /* Deal with the menu action. We first pass it to Wimp_Event to + * handle any automatic menus. If this doesn't recognise the details, + * it is passed on to the code in menus.c. + */ + + needs_prepare = false; + + if (!ro_gui_wimp_event_menu_selection(current_menu_window, + current_menu_icon, current_menu, selection, action)) { + + /* perform menu action */ + if (action != NO_ACTION) + ro_gui_menu_handle_action(current_menu_window, + action, false); + + /* perform non-automated actions */ + if (current_menu == url_suggest_menu) { + g = ro_gui_toolbar_lookup(current_menu_window); + if (g) { + url = url_suggest_menu->entries[selection->items[0]]. + data.indirected_text.text; + gui_window_set_url(g, url); + browser_window_go(g->bw, url, 0, true); + } + } else if (current_menu == gui_form_select_menu) { + g = ro_gui_window_lookup(current_menu_window); + assert(g); + + if (selection->items[0] >= 0) { + form_select_process_selection(g->bw->current_content, + gui_form_select_control, + selection->items[0]); + } } - } - /* allow automatic menus to have their data updated */ - ro_gui_wimp_event_menu_selection(current_menu_window, current_menu_icon, - current_menu, selection); + /* allow automatic menus to have their data updated */ +// ro_gui_wimp_event_menu_selection(current_menu_window, +// current_menu_icon, current_menu, selection); + + /* Menus not handled by ro_gui_wimp_event_menu_selection() + * will need to be re-prepared before an adjust reopening. + */ + + needs_prepare = true; + } /* re-open the menu for Adjust clicks */ error = xwimp_get_pointer_info(&pointer); @@ -738,24 +655,26 @@ void ro_gui_menu_selection(wimp_selection *selection) } /* re-prepare all the visible entries */ - i = 0; - menu = current_menu; - do { - j = 0; + if (needs_prepare) { + i = 0; + menu = current_menu; do { - action = ro_gui_menu_find_action(current_menu, - &menu->entries[j]); - if (action != NO_ACTION) - ro_gui_menu_prepare_action(current_menu_window, - action, false); - } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST)); - j = selection->items[i++]; - if (j != -1) { - menu = menu->entries[j].sub_menu; - if ((!menu) || (menu == wimp_NO_SUB_MENU)) - break; - } - } while (j != -1); + j = 0; + do { + action = ro_gui_menu_find_action(current_menu, + &menu->entries[j]); + if (action != NO_ACTION) + ro_gui_menu_prepare_action(current_menu_window, + action, false); + } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST)); + j = selection->items[i++]; + if (j != -1) { + menu = menu->entries[j].sub_menu; + if ((!menu) || (menu == wimp_NO_SUB_MENU)) + break; + } + } while (j != -1); + } if (current_menu == gui_form_select_menu) { assert(g); /* Keep scan-build happy */ @@ -788,28 +707,42 @@ void ro_gui_menu_warning(wimp_message_menu_warning *warning) for (i = 1; warning->selection.items[i] != -1; i++) menu_entry = &menu_entry->sub_menu-> entries[warning->selection.items[i]]; + action = ro_gui_menu_find_action(current_menu, menu_entry); - if (IS_MENU(menu_entry->sub_menu)) { - ro_gui_wimp_event_register_submenu((wimp_w)0); - sub_menu = menu_entry->sub_menu; - i = 0; - do { - action = ro_gui_menu_find_action(current_menu, - &sub_menu->entries[i]); + /* We only process the menu in the old way if the wimp_event module + * hasn't processed it for us. + */ + + if (!ro_gui_wimp_event_submenu_warning(current_menu_window, + current_menu_icon, current_menu, &(warning->selection), + action)) { + if (IS_MENU(menu_entry->sub_menu)) { + ro_gui_wimp_event_register_submenu((wimp_w)0); + sub_menu = menu_entry->sub_menu; + i = 0; + do { + action = ro_gui_menu_find_action(current_menu, + &sub_menu->entries[i]); + if (action != NO_ACTION) + ro_gui_menu_prepare_action(current_menu_window, + action, false); + } while (!(sub_menu->entries[i++].menu_flags & + wimp_MENU_LAST)); + } else { + ro_gui_wimp_event_register_submenu((wimp_w)menu_entry->sub_menu); + action = ro_gui_menu_find_action(current_menu, menu_entry); if (action != NO_ACTION) ro_gui_menu_prepare_action(current_menu_window, - action, false); - } while (!(sub_menu->entries[i++].menu_flags & wimp_MENU_LAST)); - } else { - ro_gui_wimp_event_register_submenu((wimp_w)menu_entry->sub_menu); - action = ro_gui_menu_find_action(current_menu, menu_entry); - if (action != NO_ACTION) - ro_gui_menu_prepare_action(current_menu_window, - action, true); - /* remove the close icon */ + action, true); + } + } + + /* If this is a dialogue box, remove the close and back icons. */ + + if (!(IS_MENU(menu_entry->sub_menu))) ro_gui_wimp_update_window_furniture((wimp_w)menu_entry->sub_menu, wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_BACK_ICON, 0); - } + /* open the sub-menu */ error = xwimp_create_sub_menu(menu_entry->sub_menu, @@ -829,64 +762,44 @@ void ro_gui_menu_warning(wimp_message_menu_warning *warning) */ void ro_gui_menu_refresh_toolbar(struct toolbar *toolbar) { +// struct treeview_window *treeview_window; assert(toolbar); toolbar->reformat_buttons = true; ro_gui_theme_process_toolbar(toolbar, -1); if (toolbar->type == THEME_BROWSER_TOOLBAR) { - gui_window_update_extent(ro_gui_window_lookup(current_menu_window)); - } else if (toolbar->type == THEME_HOTLIST_TOOLBAR) { - tree_resized(hotlist_tree); - xwimp_force_redraw((wimp_w)hotlist_tree->handle, - 0,-16384, 16384, 16384); - } else if (toolbar->type == THEME_HISTORY_TOOLBAR) { - tree_resized(global_history_tree); - xwimp_force_redraw((wimp_w)global_history_tree->handle, - 0,-16384, 16384, 16384); - } else if (toolbar->type == THEME_COOKIES_TOOLBAR) { - tree_resized(cookies_tree); - xwimp_force_redraw((wimp_w)cookies_tree->handle, - 0,-16384, 16384, 16384); + gui_window_update_extent(ro_gui_window_lookup( + current_menu_window)); +// } else if (toolbar->type == THEME_HOTLIST_TOOLBAR) { +// treeview_window = ro_gui_hotlist_get(); +// /* TODO: FIX THIS */ +// /* tree_resized(treeview_window->tree); */ +// xwimp_force_redraw(treeview_window->window, +// 0,-16384, 16384, 16384); +// } else if (toolbar->type == THEME_HISTORY_TOOLBAR) { +// treeview_window = ro_gui_global_history_get(); +// /* TODO: FIX THIS */ +// /* tree_resized(treeview_window->tree); */ +// xwimp_force_redraw(treeview_window->window, +// 0,-16384, 16384, 16384); +// } else if (toolbar->type == THEME_COOKIES_TOOLBAR) { +// treeview_window = ro_gui_cookies_get(); +// /* TODO: FIX THIS */ +// /* tree_resized(treeview_window->tree); */ +// xwimp_force_redraw(treeview_window->window, +// 0,-16384, 16384, 16384); } } /** - * Builds the URL suggestion menu - */ -bool ro_gui_menu_prepare_url_suggest(void) { - char **suggest_text; - int suggestions; - int i; - - suggest_text = global_history_get_recent(&suggestions); - if (suggestions < 1) - return false; - - for (i = 0; i < suggestions; i++) { - url_suggest_menu->entries[i].menu_flags = 0; - url_suggest_menu->entries[i].data.indirected_text.text = - suggest_text[i]; - url_suggest_menu->entries[i].data.indirected_text.size = - strlen(suggest_text[i]) + 1; - } - - url_suggest_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED; - url_suggest_menu->entries[suggestions - 1].menu_flags |= wimp_MENU_LAST; - return true; -} - - -/** * Update navigate menu status and toolbar icons. * * /param gui the gui_window to update */ void ro_gui_prepare_navigate(struct gui_window *gui) { - int suggestions; - ro_gui_menu_prepare_action(gui->window, HOTLIST_SHOW, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_STOP, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_RELOAD_ALL, @@ -902,9 +815,9 @@ void ro_gui_prepare_navigate(struct gui_window *gui) ro_gui_menu_prepare_action(gui->window, BROWSER_FIND_TEXT, false); if (gui->toolbar) { - global_history_get_recent(&suggestions); ro_gui_set_icon_shaded_state(gui->toolbar->toolbar_handle, - ICON_TOOLBAR_SUGGEST, (suggestions <= 0)); + ICON_TOOLBAR_SUGGEST, + !ro_gui_url_suggest_prepare_menu()); } } @@ -1122,8 +1035,10 @@ void gui_create_form_select_menu(struct browser_window *bw, /** * Creates a wimp_menu and adds it to the list to handle actions for. * - * \param menu the data to create the menu with - * \return the menu created, or NULL on failure + * \param *menu The data to create the menu with + * \param *callbacks A callback table for the menu (NULL if to be + * handled in the 'old-fashined way' by menus.c). + * \return The menu created, or NULL on failure */ wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu) { @@ -1475,13 +1390,12 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, struct browser_window *bw = NULL; hlcache_handle *h = NULL; struct toolbar *t = NULL; - struct tree *tree; - struct node *node; os_error *error; char url[80]; - const struct url_data *data; + bool is_cookies, is_hotlist, is_global_history; - ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, &tree); + ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, + &is_cookies, &is_hotlist, &is_global_history); switch (action) { @@ -1515,42 +1429,26 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, windows_at_pointer); return true; case HISTORY_SHOW_GLOBAL: - ro_gui_tree_show(global_history_tree); + ro_gui_global_history_open(); return true; /* hotlist actions */ case HOTLIST_ADD_URL: - if ((!hotlist_tree) || (!h) || (!content_get_url(h))) + if (h == NULL || content_get_url(h) == NULL) return false; - data = urldb_get_url_data(content_get_url(h)); - if (data) { - node = tree_create_URL_node(hotlist_tree->root, - content_get_url(h), - data, data->title); - if (node) { - 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); - ro_gui_hotlist_save(); - } - } + hotlist_add_page(content_get_url(h)); return true; case HOTLIST_SHOW: - ro_gui_tree_show(hotlist_tree); + ro_gui_hotlist_open(); return true; /* cookies actions */ case COOKIES_SHOW: - ro_gui_tree_show(cookies_tree); + ro_gui_cookies_open(); return true; case COOKIES_DELETE: - if (cookies_tree->root->child) - tree_delete_node(cookies_tree, cookies_tree->root->child, true); + cookies_delete_all(); return true; /* page actions */ @@ -1635,8 +1533,6 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, if (!h) return false; /* Fall through */ - case HOTLIST_EXPORT: - case HISTORY_EXPORT: ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(owner, dialog_saveas, windows_at_pointer); @@ -1788,61 +1684,6 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, ro_gui_save_options(); return true; - /* tree actions */ - case TREE_NEW_FOLDER: - ro_gui_menu_prepare_action(owner, action, true); - ro_gui_dialog_open_persistent((wimp_w)tree->handle, - dialog_folder, windows_at_pointer); - return true; - case TREE_NEW_LINK: - ro_gui_menu_prepare_action(owner, action, true); - ro_gui_dialog_open_persistent((wimp_w)tree->handle, - dialog_entry, windows_at_pointer); - return true; - case TREE_EXPAND_ALL: - case TREE_EXPAND_FOLDERS: - case TREE_EXPAND_LINKS: - tree_handle_expansion(tree, tree->root, true, - (action != TREE_EXPAND_LINKS), - (action != TREE_EXPAND_FOLDERS)); - return true; - case TREE_COLLAPSE_ALL: - case TREE_COLLAPSE_FOLDERS: - case TREE_COLLAPSE_LINKS: - tree_handle_expansion(tree, tree->root, false, - (action != TREE_COLLAPSE_LINKS), - (action != TREE_COLLAPSE_FOLDERS)); - return true; - case TREE_SELECTION_EDIT: - return true; - case TREE_SELECTION_LAUNCH: - ro_gui_tree_launch_selected(tree); - return true; - case TREE_SELECTION_DELETE: - ro_gui_tree_stop_edit(tree); - tree_delete_selected_nodes(tree, tree->root); - if (tree == hotlist_tree) - ro_gui_hotlist_save(); - ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); - ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); - return true; - case TREE_SELECT_ALL: - ro_gui_tree_stop_edit(tree); - if (tree->root->child) { - tree->temp_selection = NULL; - tree_set_node_selected(tree, tree->root, true); - } - ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); - ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); - return true; - case TREE_CLEAR_SELECTION: - tree->temp_selection = NULL; - ro_gui_tree_stop_edit(tree); - tree_set_node_selected(tree, tree->root, false); - ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); - ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); - return true; - /* toolbar actions */ case TOOLBAR_BUTTONS: assert(t); @@ -1895,21 +1736,20 @@ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, bool windows) { - struct menu_definition_entry *entry; struct gui_window *g; struct browser_window *bw; hlcache_handle *h; struct toolbar *t; - struct tree *tree; - struct node *node; bool result = false; int checksum = 0; os_error *error; char *parent; url_func_result res; bool compare; + bool is_cookies, is_hotlist, is_global_history; - ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, &tree); + ro_gui_menu_get_window_details(owner, &g, &bw, &h, &t, + &is_cookies, &is_hotlist, &is_global_history); if (current_menu_open) checksum = ro_gui_menu_get_checksum(); if (!h) { @@ -1935,35 +1775,11 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, !(h || history_back_available(bw->history) || history_forward_available(bw->history)))); break; - case HISTORY_SHOW_GLOBAL: - ro_gui_menu_set_entry_shaded(current_menu, action, - !global_history_tree); - break; /* hotlist actions */ case HOTLIST_ADD_URL: ro_gui_menu_set_entry_shaded(current_menu, action, - (!h || !hotlist_tree)); - break; - case HOTLIST_SHOW: - ro_gui_menu_set_entry_shaded(current_menu, action, - !hotlist_tree); - if ((t) && (!t->editor) && - (t->type == THEME_BROWSER_TOOLBAR)) - ro_gui_set_icon_shaded_state( - t->toolbar_handle, - ICON_TOOLBAR_BOOKMARK, - !hotlist_tree); - break; - - /* cookies actions */ - case COOKIES_SHOW: - ro_gui_menu_set_entry_shaded(current_menu, action, - !cookies_tree); - break; - case COOKIES_DELETE: - ro_gui_menu_set_entry_shaded(current_menu, action, - !(cookies_tree && cookies_tree->root->child)); + h == NULL); break; /* page actions */ @@ -2003,7 +1819,8 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, /* object actions */ case BROWSER_OBJECT: ro_gui_menu_set_entry_shaded(current_menu, action, - !current_menu_object && !current_menu_url); + !current_menu_object && + !current_menu_url); break; case BROWSER_OBJECT_LINK: @@ -2013,7 +1830,8 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, case BROWSER_OBJECT_INFO: if (windows && current_menu_object) - ro_gui_menu_prepare_objectinfo(current_menu_object, + ro_gui_menu_prepare_objectinfo( + current_menu_object, current_menu_url); /* Fall through */ case BROWSER_OBJECT_RELOAD: @@ -2024,7 +1842,8 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, case BROWSER_OBJECT_PRINT: /* not yet implemented */ - ro_gui_menu_set_entry_shaded(current_menu, action, true); + ro_gui_menu_set_entry_shaded(current_menu, action, + true); break; /* save actions (browser, hotlist, history) */ @@ -2032,7 +1851,8 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, ro_gui_menu_set_entry_shaded(current_menu, action, !current_menu_object); if (windows && current_menu_object) - ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG, current_menu_object, + ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG, + current_menu_object, NULL, NULL, NULL); break; case BROWSER_OBJECT_EXPORT: @@ -2047,41 +1867,41 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, if (h) { switch (content_get_type(h)) { /* \todo - this classification should prob be done in content_() */ - /* bitmap types (Sprite export possible) */ + /* bitmap types (Sprite export possible) */ #ifdef WITH_JPEG - case CONTENT_JPEG: + case CONTENT_JPEG: #endif #ifdef WITH_MNG - case CONTENT_JNG: - case CONTENT_MNG: + case CONTENT_JNG: + case CONTENT_MNG: #endif #ifdef WITH_GIF - case CONTENT_GIF: + case CONTENT_GIF: #endif #ifdef WITH_BMP - case CONTENT_BMP: - case CONTENT_ICO: + case CONTENT_BMP: + case CONTENT_ICO: #endif #if defined(WITH_MNG) || defined(WITH_PNG) - case CONTENT_PNG: + case CONTENT_PNG: #endif #ifdef WITH_SPRITE - case CONTENT_SPRITE: + case CONTENT_SPRITE: #endif - exp_sprite = true; - break; + exp_sprite = true; + break; - /* vector types (Draw export possible) */ + /* vector types (Draw export possible) */ #if defined(WITH_NS_SVG) || defined(WITH_RSVG) - case CONTENT_SVG: + case CONTENT_SVG: #endif #ifdef WITH_DRAW - case CONTENT_DRAW: + case CONTENT_DRAW: #endif - exp_draw = true; - break; + exp_draw = true; + break; - default: break; + default: break; } } @@ -2229,16 +2049,6 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, content_get_url(h), content_get_title(h)); break; - case HOTLIST_EXPORT: - if ((tree) && (windows)) - ro_gui_save_prepare(GUI_SAVE_HOTLIST_EXPORT_HTML, - NULL, NULL, NULL, NULL); - break; - case HISTORY_EXPORT: - if ((tree) && (windows)) - ro_gui_save_prepare(GUI_SAVE_HISTORY_EXPORT_HTML, - NULL, NULL, NULL, NULL); - break; /* navigation actions */ case BROWSER_NAVIGATE_BACK: @@ -2379,82 +2189,7 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, break; /* tree actions */ - case TREE_NEW_FOLDER: - ro_gui_hotlist_prepare_folder_dialog(NULL); - break; - case TREE_NEW_LINK: - ro_gui_hotlist_prepare_entry_dialog(NULL); - break; - case TREE_EXPAND_ALL: - case TREE_EXPAND_FOLDERS: - case TREE_EXPAND_LINKS: - case TREE_COLLAPSE_ALL: - case TREE_COLLAPSE_FOLDERS: - case TREE_COLLAPSE_LINKS: - if ((tree) && (tree->root)) { - ro_gui_menu_set_entry_shaded(current_menu, - action, !tree->root->child); - - if ((t) && (!t->editor) && (t->type != - THEME_BROWSER_TOOLBAR)) { - ro_gui_set_icon_shaded_state( - t->toolbar_handle, - ICON_TOOLBAR_EXPAND, - !tree->root->child); - ro_gui_set_icon_shaded_state( - t->toolbar_handle, - ICON_TOOLBAR_OPEN, - !tree->root->child); - } - } - break; case TREE_SELECTION: - if ((!tree) || (!tree->root)) - break; - if (tree->root->child) - result = tree_has_selection(tree->root->child); - ro_gui_menu_set_entry_shaded(current_menu, - action, !result); - if ((t) && (!t->editor) && - (t->type != THEME_BROWSER_TOOLBAR)) { - ro_gui_set_icon_shaded_state( - t->toolbar_handle, - ICON_TOOLBAR_DELETE, !result); - ro_gui_set_icon_shaded_state( - t->toolbar_handle, - ICON_TOOLBAR_LAUNCH, !result); - } - break; - case TREE_SELECTION_EDIT: - node = tree_get_selected_node(tree->root); - entry = ro_gui_menu_find_entry(current_menu, action); - if ((!node) || (!entry)) - break; - if (node->folder) { - entry->menu_entry->sub_menu = - (wimp_menu *)dialog_folder; - if (windows) - ro_gui_hotlist_prepare_folder_dialog(node); - } else { - entry->menu_entry->sub_menu = - (wimp_menu *)dialog_entry; - if (windows) - ro_gui_hotlist_prepare_entry_dialog(node); - } - break; - case TREE_SELECTION_LAUNCH: - case TREE_SELECTION_DELETE: - case TREE_CLEAR_SELECTION: - if ((!tree) || (!tree->root)) - break; - if (tree->root->child) - result = tree_has_selection(tree->root->child); - ro_gui_menu_set_entry_shaded(current_menu, - action, !result); - break; - case TREE_SELECT_ALL: - ro_gui_menu_set_entry_shaded(current_menu, action, - !tree->root->child); break; /* toolbar actions */ @@ -2509,31 +2244,35 @@ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, */ void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g, struct browser_window **bw, hlcache_handle **h, - struct toolbar **toolbar, struct tree **tree) + struct toolbar **toolbar, + bool *is_cookies, bool *is_hotlist, bool *is_global_history) { + *is_cookies = false; + *is_hotlist = false; + *is_global_history = false; + *g = ro_gui_window_lookup(w); + if (*g) { *bw = (*g)->bw; *toolbar = (*g)->toolbar; if (*bw) *h = (*bw)->current_content; - *tree = NULL; } else { *bw = NULL; *h = NULL; - if ((hotlist_tree) && (w == (wimp_w)hotlist_tree->handle)) - *tree = hotlist_tree; - else if ((global_history_tree) && - (w == (wimp_w)global_history_tree->handle)) - *tree = global_history_tree; - else if ((cookies_tree) && (w == (wimp_w)cookies_tree->handle)) - *tree = cookies_tree; - else - *tree = NULL; - if (*tree) - *toolbar = (*tree)->toolbar; - else + if (ro_gui_hotlist_check_window(w)) { + *is_hotlist = true; +// *toolbar = treeview_window->toolbar; + } else if (ro_gui_global_history_check_window(w)) { + *is_global_history = true; +// *toolbar = treeview_window->toolbar; + } else if (ro_gui_cookies_check_window(w)) { + *is_cookies = true; +// *toolbar = treeview_window->toolbar; + } else { *toolbar = NULL; + } } } @@ -2595,6 +2334,7 @@ bool ro_gui_menu_translate(struct menu_definition *menu) char *translated; utf8_convert_ret err; + /* read current alphabet */ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet); if (error) { diff --git a/riscos/menus.h b/riscos/menus.h index ec72cbf83..7cb6a4c3f 100644 --- a/riscos/menus.h +++ b/riscos/menus.h @@ -155,6 +155,20 @@ typedef enum { } menu_action; +/* Menu entry structures for use when defining menus. */ + +struct ns_menu_entry { + const char *text; /**< menu text (from messages) */ + menu_action action; /**< associated action */ + wimp_w *sub_window; /**< sub-window if any */ +}; + +struct ns_menu { + const char *title; + struct ns_menu_entry entries[]; +}; + + void ro_gui_menu_init(void); void ro_gui_menu_create(wimp_menu* menu, int x, int y, wimp_w w); bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, @@ -171,4 +185,10 @@ void ro_gui_prepare_navigate(struct gui_window *gui); const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu, const char *translated); +wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu); +void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action, + bool shaded); +void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action, + bool ticked); + #endif diff --git a/riscos/save.c b/riscos/save.c index a222b6df9..2430c13aa 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -37,6 +37,8 @@ #include "oslib/wimpspriteop.h" #include "content/content.h" #include "content/hlcache.h" +#include "desktop/hotlist.h" +#include "desktop/history_global_core.h" #include "desktop/netsurf.h" #include "desktop/save_complete.h" #include "desktop/save_text.h" @@ -630,9 +632,9 @@ void ro_gui_save_drag_end(wimp_dragged *drag) while (!dest_ok && (box = box_at_point(box, pos.x, pos.y, &box_x, &box_y, &h))) { - if (box->style && + if (box->style && css_computed_visibility( - box->style) == + box->style) == CSS_VISIBILITY_HIDDEN) continue; @@ -897,8 +899,7 @@ bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite) LINK_TEXT, path); case GUI_SAVE_HOTLIST_EXPORT_HTML: - if (!options_save_tree(hotlist_tree, path, - "NetSurf hotlist")) + if (!hotlist_export(path)) return false; error = xosfile_set_type(path, 0xfaf); if (error) @@ -906,8 +907,7 @@ bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite) error->errnum, error->errmess)); break; case GUI_SAVE_HISTORY_EXPORT_HTML: - if (!options_save_tree(global_history_tree, path, - "NetSurf history")) + if (!history_global_export(path)) return false; error = xosfile_set_type(path, 0xfaf); if (error) @@ -1346,7 +1346,7 @@ void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type, } /* leafname */ - if (url && url_nice(url, &nice, option_strip_extensions) == + if (url && url_nice(url, &nice, option_strip_extensions) == URL_FUNC_OK) { for (i = 0; nice[i]; i++) { if (nice[i] == '.') diff --git a/riscos/sslcert.c b/riscos/sslcert.c index 5c77f31d5..990c2501c 100644 --- a/riscos/sslcert.c +++ b/riscos/sslcert.c @@ -1,5 +1,6 @@ /* * Copyright 2006 John M Bell <jmb202@ecs.soton.ac.uk> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -32,9 +33,11 @@ #include "content/fetch.h" #include "content/urldb.h" #include "desktop/browser.h" +#include "desktop/sslcert.h" #include "desktop/gui.h" #include "desktop/tree.h" #include "riscos/dialog.h" +#include "riscos/sslcert.h" #include "riscos/textarea.h" #include "riscos/treeview.h" #include "riscos/wimp.h" @@ -47,50 +50,36 @@ #define ICON_SSL_REJECT 3 #define ICON_SSL_ACCEPT 4 -#define ICON_CERT_VERSION 3 -#define ICON_CERT_VALID_FROM 5 -#define ICON_CERT_TYPE 7 -#define ICON_CERT_VALID_TO 9 -#define ICON_CERT_SERIAL 11 -#define ICON_CERT_ISSUER 13 -#define ICON_CERT_SUBJECT 15 - -static wimp_window *dialog_tree_template; -static wimp_window *dialog_cert_template; -static wimp_window *dialog_display_template; - -struct session_data { - struct session_cert *certs; - unsigned long num; - char *url; - struct tree *tree; - - nserror (*cb)(bool proceed, void *pw); - void *cbpw; -}; -struct session_cert { - char version[16], valid_from[32], valid_to[32], type[8], serial[32]; - char *issuer_t; - char *subject_t; - uintptr_t issuer; - uintptr_t subject; +static wimp_window *ro_gui_cert_dialog_template; +static wimp_window *ro_gui_cert_tree_template; + +struct ro_sslcert +{ + wimp_w window; + wimp_w pane; + ro_treeview *tv; + struct sslcert_session_data *data; }; -static bool ro_gui_cert_click(wimp_pointer *pointer); -static void ro_gui_cert_close(wimp_w w); -static bool ro_gui_cert_apply(wimp_w w); +static void ro_gui_cert_accept(wimp_pointer *pointer); +static void ro_gui_cert_reject(wimp_pointer *pointer); +static void ro_gui_cert_close_window(wimp_w w); +static void ro_gui_cert_release_window(struct ro_sslcert *s); /** - * Load the cert window template + * Load and initialise the certificate window template */ -void ro_gui_cert_init(void) +void ro_gui_cert_preinitialise(void) { - dialog_tree_template = ro_gui_dialog_load_template("tree"); - dialog_cert_template = ro_gui_dialog_load_template("sslcert"); - dialog_display_template = ro_gui_dialog_load_template("ssldisplay"); + /* Load templates for the SSL windows and adjust the tree window + * flags to suit. + */ + + ro_gui_cert_dialog_template = ro_gui_dialog_load_template("sslcert"); + ro_gui_cert_tree_template = ro_gui_dialog_load_template("tree"); - dialog_tree_template->flags &= ~(wimp_WINDOW_MOVEABLE | + ro_gui_cert_tree_template->flags &= ~(wimp_WINDOW_MOVEABLE | wimp_WINDOW_BACK_ICON | wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_TITLE_ICON | @@ -99,169 +88,169 @@ void ro_gui_cert_init(void) } /** + * Load and initialise the certificate window template + */ + +void ro_gui_cert_postinitialise(void) +{ + /* Initialise the SSL module. */ + + sslcert_init(); +} + +/** * Open the certificate verification dialog + * + * \param *bw The browser window owning the certificates. + * \param *c The content data corresponding to the + * certificates. + * \param *certs The certificate details. + * \param num The number of certificates included. */ void gui_cert_verify(const char *url, const struct ssl_cert_info *certs, unsigned long num, nserror (*cb)(bool proceed, void *pw), void *cbpw) { - wimp_w w; - wimp_w ssl_w; - const struct ssl_cert_info *from; - struct session_cert *to; - struct session_data *data; - struct tree *tree; - struct node *node; - wimp_window_state state; - wimp_icon_state istate; - os_error *error; - long i; + struct ro_sslcert *sslcert_window; + wimp_window_state state; + wimp_icon_state istate; + wimp_window_info info; + os_error *error; + bool set_extent; assert(certs); - /* copy the certificate information */ - data = calloc(1, sizeof(struct session_data)); - if (!data) { - warn_user("NoMemory", 0); + sslcert_window = malloc(sizeof(struct ro_sslcert)); + if (sslcert_window == NULL) { + LOG(("Failed to allocate memory for SSL Cert Dialog")); return; } - data->url = strdup(url); - if (!data->url) { - free(data); - warn_user("NoMemory", 0); - return; - } - data->cb = cb; - data->cbpw = cbpw; - data->num = num; - data->certs = calloc(num, sizeof(struct session_cert)); - if (!data->certs) { - free(data->url); - free(data); - warn_user("NoMemory", 0); - return; - } - for (i = 0; i < (long)num; i++) { - to = &data->certs[i]; - from = &certs[i]; - to->subject_t = strdup(from->subject); - to->issuer_t = strdup(from->issuer); - if ((!to->subject_t) || (!to->issuer_t)) { - for (; i >= 0; i--) { - to = &data->certs[i]; - free(to->subject_t); - free(to->issuer_t); - } - free(data->certs); - free(data->url); - free(data); - warn_user("NoMemory", 0); - return; - } - snprintf(to->version, sizeof data->certs->version, "%ld", - from->version); - snprintf(to->valid_from, sizeof data->certs->valid_from, "%s", - from->not_before); - snprintf(to->type, sizeof data->certs->type, "%d", - from->cert_type); - snprintf(to->valid_to, sizeof data->certs->valid_to, "%s", - from->not_after); - snprintf(to->serial, sizeof data->certs->serial, "%ld", - from->serial); - } - /* create the SSL window */ - error = xwimp_create_window(dialog_cert_template, &ssl_w); + /* Create the SSL window and its pane. */ + + error = xwimp_create_window(ro_gui_cert_dialog_template, + &(sslcert_window->window)); if (error) { - free(data->certs); - free(data->url); - free(data); LOG(("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess)); + free(sslcert_window); return; } - /* automated SSL window event handling */ - ro_gui_wimp_event_set_user_data(ssl_w, data); - ro_gui_wimp_event_register_cancel(ssl_w, ICON_SSL_REJECT); - ro_gui_wimp_event_register_ok(ssl_w, ICON_SSL_ACCEPT, ro_gui_cert_apply); - ro_gui_dialog_open_persistent(NULL, ssl_w, false); - - /* create a tree window (styled as a list) */ - error = xwimp_create_window(dialog_tree_template, &w); + error = xwimp_create_window(ro_gui_cert_tree_template, + &(sslcert_window->pane)); if (error) { - ro_gui_cert_close(ssl_w); LOG(("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess)); + free(sslcert_window); return; } - tree = calloc(sizeof(struct tree), 1); - if (!tree) { - ro_gui_cert_close(ssl_w); - warn_user("NoMemory", 0); + + /* Create the SSL data and build a tree from it. */ + + sslcert_window->tv = ro_treeview_create(sslcert_window->pane, NULL, + sslcert_get_tree_flags()); + if (sslcert_window->tv == NULL) { + LOG(("Failed to allocate treeview")); + free(sslcert_window); return; } - tree->root = tree_create_folder_node(NULL, "Root"); - if (!tree->root) { - ro_gui_cert_close(ssl_w); - warn_user("NoMemory", 0); - free(tree); - tree = NULL; + + sslcert_window->data = sslcert_create_session_data(num, url, cb, cbpw); + sslcert_load_tree(ro_treeview_get_tree(sslcert_window->tv), + certs, sslcert_window->data); + + tree_set_redraw(ro_treeview_get_tree(sslcert_window->tv), true); + + /* Set up the certificate window event handling. + * + * (The action buttons are registered as button events, not OK and + * Cancel, as both need to carry out actions.) + */ + + ro_gui_wimp_event_set_user_data(sslcert_window->window, sslcert_window); + ro_gui_wimp_event_register_close_window(sslcert_window->window, + ro_gui_cert_close_window); + ro_gui_wimp_event_register_button(sslcert_window->window, + ICON_SSL_REJECT, ro_gui_cert_reject); + ro_gui_wimp_event_register_button(sslcert_window->window, + ICON_SSL_ACCEPT, ro_gui_cert_accept); + + ro_gui_dialog_open_persistent(NULL, sslcert_window->window, false); + + /* Nest the tree window inside the pane window. To do this, we: + * - Get the current pane extent, + * - Get the parent window position and the location of the pane- + * locating icon inside it, + * - Set the visible area of the pane to suit, + * - Check that the pane extents are OK for this visible area, and + * increase them if necessary, + * - Before finally opening the pane as a nested part of the parent. + */ + + info.w = sslcert_window->pane; + error = xwimp_get_window_info_header_only(&info); + if (error) { + ro_gui_cert_release_window(sslcert_window); + LOG(("xwimp_get_window_info: 0x%x: %s", + error->errnum, error->errmess)); return; } - tree->root->expanded = true; - tree->handle = (int)w; - tree->movable = false; - tree->no_drag = true; - tree->no_vscroll = true; - tree->no_furniture = true; - tree->single_selection = true; - data->tree = tree; - - /* put the SSL names in the tree */ - for (i = 0; i < (long)num; i++) { - node = tree_create_leaf_node(tree->root, certs[i].subject); - if (node) { - node->data.data = TREE_ELEMENT_SSL; - tree_set_node_sprite(node, "small_xxx", "small_xxx"); - } - } - - /* automated treeview event handling */ - ro_gui_wimp_event_set_user_data(w, tree); - ro_gui_wimp_event_register_keypress(w, ro_gui_tree_keypress); - ro_gui_wimp_event_register_redraw_window(w, ro_gui_tree_redraw); - ro_gui_wimp_event_register_open_window(w, ro_gui_tree_open); - ro_gui_wimp_event_register_close_window(w, ro_gui_wimp_event_finalise); - ro_gui_wimp_event_register_mouse_click(w, ro_gui_cert_click); - /* nest the tree window inside the pane window */ - state.w = ssl_w; + state.w = sslcert_window->window; error = xwimp_get_window_state(&state); if (error) { - ro_gui_cert_close(ssl_w); + ro_gui_cert_release_window(sslcert_window); LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); return; } - istate.w = ssl_w; + istate.w = sslcert_window->window; istate.i = ICON_SSL_PANE; error = xwimp_get_icon_state(&istate); if (error) { - ro_gui_cert_close(ssl_w); + ro_gui_cert_release_window(sslcert_window); LOG(("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess)); return; } - state.w = w; + + state.w = sslcert_window->pane; state.visible.x1 = state.visible.x0 + istate.icon.extent.x1 - 20 - - ro_get_vscroll_width(w); + ro_get_vscroll_width(sslcert_window->pane); state.visible.x0 += istate.icon.extent.x0 + 20; state.visible.y0 = state.visible.y1 + istate.icon.extent.y0 + 20; state.visible.y1 += istate.icon.extent.y1 - 32; - error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), ssl_w, + + set_extent = false; + + if ((info.extent.x1 - info.extent.x0) < + (state.visible.x1 - state.visible.x0)) { + info.extent.x0 = 0; + info.extent.x1 = state.visible.x1 - state.visible.x0; + set_extent = true; + } + if ((info.extent.y1 - info.extent.y0) < + (state.visible.y1 - state.visible.y0)) { + info.extent.y1 = 0; + info.extent.x1 = state.visible.y0 - state.visible.y1; + set_extent = true; + } + + if (set_extent) { + error = xwimp_set_extent(sslcert_window->pane, &(info.extent)); + if (error) { + ro_gui_cert_release_window(sslcert_window); + LOG(("xwimp_set_extent: 0x%x: %s", + error->errnum, error->errmess)); + return; + } + } + + error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), + sslcert_window->window, wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_XORIGIN_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT @@ -271,192 +260,100 @@ void gui_cert_verify(const char *url, wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_RS_EDGE_SHIFT); if (error) { - ro_gui_cert_close(ssl_w); + ro_gui_cert_release_window(sslcert_window); LOG(("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess)); + ro_gui_cert_release_window(sslcert_window); return; } - tree_initialise(tree); } -void ro_gui_cert_open(struct tree *tree, struct node *node) -{ - struct node *n; - struct session_data *data; - struct session_cert *session; - wimp_window_state state; - wimp_w child; - wimp_w parent; - wimp_w w; - unsigned long i; - os_error *error; - - assert(tree->root); +/** + * Handle acceptance of certificate via event callback. + * + * \param *pointer The wimp pointer block. + */ - /* firstly we need to get our node index in the list */ - for (n = tree->root->child, i = 0; n; i++, n = n->next) - if (n == node) - break; - assert(n); +void ro_gui_cert_accept(wimp_pointer *pointer) +{ + struct ro_sslcert *s; - /* now we get the handle of our list window */ - child = (wimp_w)tree->handle; - assert(child); + s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(pointer->w); - /* now we can get the linked parent handle */ - state.w = child; - error = xwimp_get_window_state_and_nesting(&state, &parent, 0); - if (error) { - LOG(("xwimp_get_window_state: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - return; - } - assert(parent); - - /* from this we can get our session data */ - data = (struct session_data *)ro_gui_wimp_event_get_user_data(parent); - assert(data); - assert(data->tree == tree); - - /* and finally the nodes session certificate data */ - session = &data->certs[i]; - assert(session); - - dialog_display_template->icons[ICON_CERT_VERSION].data.indirected_text.text = session->version; - dialog_display_template->icons[ICON_CERT_VERSION].data.indirected_text.size = strlen(session->version) + 1; - dialog_display_template->icons[ICON_CERT_VALID_FROM].data.indirected_text.text = session->valid_from; - dialog_display_template->icons[ICON_CERT_VALID_FROM].data.indirected_text.size = strlen(session->valid_from) + 1; - dialog_display_template->icons[ICON_CERT_TYPE].data.indirected_text.text = session->type; - dialog_display_template->icons[ICON_CERT_TYPE].data.indirected_text.size = strlen(session->type) + 1; - dialog_display_template->icons[ICON_CERT_VALID_TO].data.indirected_text.text = session->valid_to; - dialog_display_template->icons[ICON_CERT_VALID_TO].data.indirected_text.size = strlen(session->valid_to) + 1; - dialog_display_template->icons[ICON_CERT_SERIAL].data.indirected_text.text = session->serial; - dialog_display_template->icons[ICON_CERT_SERIAL].data.indirected_text.size = strlen(session->serial) + 1; - - error = xwimp_create_window(dialog_display_template, &w); - if (error) { - LOG(("xwimp_create_window: 0x%x: %s", - error->errnum, error->errmess)); - free(session); - warn_user("MiscError", error->errmess); - return; + if (s != NULL) { + sslcert_accept(s->data); + ro_gui_dialog_close(s->window); + ro_gui_cert_release_window(s); } - if (session->issuer) - ro_textarea_destroy(session->issuer); - session->issuer = ro_textarea_create(w, ICON_CERT_ISSUER, - TEXTAREA_MULTILINE | TEXTAREA_READONLY, - ro_gui_desktop_font_family, ro_gui_desktop_font_size, - ro_gui_desktop_font_style); - if (!session->issuer) { - xwimp_delete_window(w); - warn_user("NoMemory", 0); - return; - } - if (!ro_textarea_set_text(session->issuer, session->issuer_t)) { - ro_textarea_destroy(session->issuer); - xwimp_delete_window(w); - warn_user("NoMemory", 0); - return; - } - - if (session->subject) - ro_textarea_destroy(session->subject); - session->subject = ro_textarea_create(w, ICON_CERT_SUBJECT, - TEXTAREA_MULTILINE | TEXTAREA_READONLY, - ro_gui_desktop_font_family, ro_gui_desktop_font_size, - ro_gui_desktop_font_style); - if (!session->subject) { - ro_textarea_destroy(session->issuer); - xwimp_delete_window(w); - warn_user("NoMemory", 0); - return; - } - if (!ro_textarea_set_text(session->subject, session->subject_t)) { - ro_textarea_destroy(session->subject); - ro_textarea_destroy(session->issuer); - xwimp_delete_window(w); - warn_user("NoMemory", 0); - return; - } - ro_gui_wimp_event_register_close_window(w, ro_gui_wimp_event_finalise); - ro_gui_dialog_open_persistent(parent, w, false); } /** - * Handle closing of certificate verification dialog + * Handle rejection of certificate via event callback. + * + * \param w The wimp pointer block. */ -void ro_gui_cert_close(wimp_w w) -{ - struct session_data *data; - os_error *error; - unsigned long i; - data = (struct session_data *)ro_gui_wimp_event_get_user_data(w); - assert(data); +void ro_gui_cert_reject(wimp_pointer *pointer) +{ + struct ro_sslcert *s; - /* If we didn't accept the certificate, send failure response */ - if (data->cb != NULL) - data->cb(false, data->cbpw); + s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(pointer->w); - for (i = 0; i < data->num; i++) { - if (data->certs[i].subject) - ro_textarea_destroy(data->certs[i].subject); - if (data->certs[i].issuer) - ro_textarea_destroy(data->certs[i].issuer); - } - free(data->certs); - free(data->url); - if (data->tree) { - tree_delete_node(data->tree, data->tree->root, false); - ro_gui_dialog_close((wimp_w)data->tree->handle); - error = xwimp_delete_window((wimp_w)data->tree->handle); - if (error) { - LOG(("xwimp_delete_window: 0x%x:%s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - } - ro_gui_wimp_event_finalise((wimp_w)data->tree->handle); - free(data->tree); + if (s != NULL) { + sslcert_reject(s->data); + ro_gui_dialog_close(s->window); + ro_gui_cert_release_window(s); } - free(data); +} - ro_gui_dialog_close(w); - error = xwimp_delete_window(w); - if (error) { - LOG(("xwimp_delete_window: 0x%x:%s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - } +/** + * Callback to handle the closure of the SSL dialogue by other means. + * + * \param w The window being closed. + */ +static void ro_gui_cert_close_window(wimp_w w) +{ + struct ro_sslcert *s; + + s = (struct ro_sslcert *) ro_gui_wimp_event_get_user_data(w); + + if (s != NULL) + ro_gui_cert_release_window(s); } /** - * Handle acceptance of certificate + * Handle closing of the RISC OS certificate verification dialog, deleting + * the windows and freeing up the treeview and data block. + * + * \param *s The data block associated with the dialogue. */ -bool ro_gui_cert_apply(wimp_w w) -{ - struct session_data *session; - session = (struct session_data *)ro_gui_wimp_event_get_user_data(w); - assert(session); +void ro_gui_cert_release_window(struct ro_sslcert *s) +{ + os_error *error; - urldb_set_cert_permissions(session->url, true); - session->cb(true, session->cbpw); + if (s == NULL) + return; - /* Flag that we sent response by invalidating callback details */ - session->cb = NULL; - session->cbpw = NULL; + LOG(("Releasing SSL data: 0x%x", (unsigned) s)); - return true; -} + ro_gui_wimp_event_finalise(s->window); + ro_treeview_destroy(s->tv); -bool ro_gui_cert_click(wimp_pointer *pointer) -{ - struct tree *tree; + error = xwimp_delete_window(s->window); + if (error) { + LOG(("xwimp_delete_window: 0x%x:%s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } + error = xwimp_delete_window(s->pane); + if (error) { + LOG(("xwimp_delete_window: 0x%x:%s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + } - tree = (struct tree *)ro_gui_wimp_event_get_user_data(pointer->w); - ro_gui_tree_click(pointer, tree); - return true; + free(s); } diff --git a/riscos/sslcert.h b/riscos/sslcert.h new file mode 100644 index 000000000..7fba9d3f2 --- /dev/null +++ b/riscos/sslcert.h @@ -0,0 +1,32 @@ +/* + * Copyright 2006 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * SSL certificate viewer (interface). + */ + +#ifndef _NETSURF_RISCOS_SSLCERT_H_ +#define _NETSURF_RISCOS_SSLCERT_H_ + +void ro_gui_cert_preinitialise(void); +void ro_gui_cert_postinitialise(void); +void ro_gui_cert_open(struct tree *tree, struct node *node); + +#endif + diff --git a/riscos/textselection.c b/riscos/textselection.c index 659169741..369346ca0 100644 --- a/riscos/textselection.c +++ b/riscos/textselection.c @@ -183,7 +183,8 @@ void ro_gui_selection_drag_end(struct gui_window *g, wimp_dragged *drag) if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos)) browser_window_mouse_drag_end(g->bw, - ro_gui_mouse_click_state(pointer.buttons), + ro_gui_mouse_click_state(pointer.buttons, + wimp_BUTTON_CLICK_DRAG), pos.x, pos.y); } @@ -542,8 +543,8 @@ void ro_gui_selection_dragging(wimp_message *message) int box_x = 0; int box_y = 0; - /* with autoscrolling, we will probably need to remember the - * gui_window and override the drag->w window handle which + /* with autoscrolling, we will probably need to remember the + * gui_window and override the drag->w window handle which * could be any window on the desktop */ g = ro_gui_window_lookup(drag->w); @@ -566,14 +567,14 @@ void ro_gui_selection_dragging(wimp_message *message) html_get_box_tree(h)) { struct box *box = html_get_box_tree(h); - while ((box = box_at_point(box, pos.x, pos.y, + while ((box = box_at_point(box, pos.x, pos.y, &box_x, &box_y, &h))) { - if (box->style && - css_computed_visibility(box->style) == + if (box->style && + css_computed_visibility(box->style) == CSS_VISIBILITY_HIDDEN) continue; - if (box->gadget && + if (box->gadget && box->gadget->type == GADGET_TEXTAREA) { textarea = box; gadget_box_x = box_x; @@ -594,10 +595,10 @@ void ro_gui_selection_dragging(wimp_message *message) gui_window_set_pointer(g, GUI_POINTER_CARET); text_box = textarea_get_position(textarea, pos.x - gadget_box_x, - pos.y - gadget_box_y, + pos.y - gadget_box_y, &char_offset, &pixel_offset); - caret_set_position(&ghost_caret, bw, text_box, + caret_set_position(&ghost_caret, bw, text_box, char_offset, pixel_offset); drag_claimed = true; @@ -613,16 +614,16 @@ void ro_gui_selection_dragging(wimp_message *message) wimp_full_message_drag_claim claim; os_error *error; - claim.size = + claim.size = offsetof(wimp_full_message_drag_claim, file_types) + 8; claim.your_ref = drag->my_ref; claim.action = message_DRAG_CLAIM; - claim.flags = wimp_DRAG_CLAIM_POINTER_CHANGED | + claim.flags = wimp_DRAG_CLAIM_POINTER_CHANGED | wimp_DRAG_CLAIM_SUPPRESS_DRAGBOX; claim.file_types[0] = osfile_TYPE_TEXT; claim.file_types[1] = ~0; - error = xwimp_send_message(wimp_USER_MESSAGE, + error = xwimp_send_message(wimp_USER_MESSAGE, (wimp_message *) &claim, drag->sender); if (error) { LOG(("xwimp_send_message: 0x%x: %s", diff --git a/riscos/theme.c b/riscos/theme.c index 648399e9f..3f142cdd3 100644 --- a/riscos/theme.c +++ b/riscos/theme.c @@ -38,8 +38,11 @@ #include "oslib/wimpspriteop.h" #include "content/content.h" #include "desktop/gui.h" +#include "riscos/cookies.h" #include "riscos/dialog.h" +#include "riscos/global_history.h" #include "riscos/gui.h" +#include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/options.h" #include "riscos/theme.h" @@ -585,9 +588,9 @@ bool ro_gui_theme_apply(struct theme_descriptor *descriptor) /* apply the theme to all the current windows */ ro_gui_window_update_theme(); - ro_gui_tree_update_theme(hotlist_tree); - ro_gui_tree_update_theme(global_history_tree); - ro_gui_tree_update_theme(cookies_tree); + ro_gui_cookies_update_theme(); + ro_gui_global_history_update_theme(); + ro_gui_hotlist_update_theme(); ro_gui_theme_close(theme_previous, false); return true; } @@ -944,14 +947,16 @@ bool ro_gui_theme_update_toolbar(struct theme_descriptor *descriptor, ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle, ro_gui_toolbar_click); break; - case THEME_HOTLIST_TOOLBAR: - case THEME_HOTLIST_EDIT_TOOLBAR: - case THEME_HISTORY_TOOLBAR: - case THEME_HISTORY_EDIT_TOOLBAR: - case THEME_COOKIES_TOOLBAR: - case THEME_COOKIES_EDIT_TOOLBAR: - ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle, - ro_gui_tree_toolbar_click); + case THEME_HOTLIST_TOOLBAR: + case THEME_HOTLIST_EDIT_TOOLBAR: + case THEME_HISTORY_TOOLBAR: + case THEME_HISTORY_EDIT_TOOLBAR: + case THEME_COOKIES_TOOLBAR: + case THEME_COOKIES_EDIT_TOOLBAR: + ro_gui_wimp_event_register_mouse_click(toolbar->toolbar_handle, + ro_gui_treeview_toolbar_click); + break; + default: break; } @@ -1322,6 +1327,7 @@ bool ro_gui_theme_process_toolbar(struct toolbar *toolbar, int width) xwimp_force_redraw(toolbar->parent_handle, 0, -16384, 16384, 16384); } + } /* Reformat the buttons starting with the throbber diff --git a/riscos/treeview.c b/riscos/treeview.c index ef23c5e3e..c992fdda7 100644 --- a/riscos/treeview.c +++ b/riscos/treeview.c @@ -1,5 +1,6 @@ /* * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -20,6 +21,8 @@ * Generic tree handling (implementation). */ +#include <oslib/os.h> + #include <assert.h> #include <stdbool.h> #include <stdint.h> @@ -35,7 +38,9 @@ #include "content/urldb.h" #include "desktop/browser.h" #include "desktop/plotters.h" +#include "desktop/textinput.h" #include "desktop/tree.h" +#include "desktop/tree_url_node.h" #include "riscos/bitmap.h" #include "riscos/dialog.h" #include "riscos/gui.h" @@ -56,1379 +61,746 @@ #define wimp_KEY_END wimp_KEY_COPY #endif -#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 tree *tree, struct node *node, bool all); -static bool ro_gui_tree_launch_node(struct tree *tree, struct node *node); -static void tree_handle_node_changed_callback(void *p); - - -/* 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[] = ""; +/** \todo Ugh! */ +const char tree_directory_icon_name[] = "directory.png"; +const char tree_content_icon_name[] = "content.png"; -/* 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]; - -/* callback update */ -struct node_update { - struct tree *tree; - struct node *node; -}; - - -/** - * Performs any initialisation for tree rendering - */ -bool ro_gui_tree_initialise(void) +struct ro_treeview { - 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; -} + struct tree *tree; /*< Pointer to treeview tree block. */ + wimp_w w; /*< RO Window Handle for tree window. */ + struct toolbar *tb; /*< Pointer to toolbar block. */ + struct { + int x; /*< X origin of tree, to RO work area. */ + int y; /*< Y origin of tree, to RO work area. */ + } origin; + struct { + int x; /*< X dimension of the tree, in RO units. */ + int y; /*< Y dimension of the tree, in RO units. */ + } size; /* (Dimensions are 0 until set correctly). */ + struct { + int x; /*< X extent of the window, in RO units. */ + int y; /*< Y extent of the window, in RO units. */ + } extent; /* (Extents are 0 until set correctly). */ + struct { + int x; /*< X coordinate of drag start */ + int y; /*< Y coordinate of drag start */ + } drag_start; + bool drag; /*< True if there's a drag going on */ +}; +static void ro_treeview_redraw_request(int x, int y, int width, int height, + void *pw); +static void ro_treeview_resized(struct tree *tree, int width, int height, + void *pw); +static void ro_treeview_scroll_visible(int y, int height, void *pw); +static void ro_treeview_get_window_dimensions(int *width, int *height, + void *pw); + +static void ro_treeview_redraw(wimp_draw *redraw); +static void ro_treeview_redraw_loop(wimp_draw *redraw, ro_treeview *tv, + osbool more); +static void ro_treeview_open(wimp_open *open); +static bool ro_treeview_mouse_click(wimp_pointer *pointer); +static bool ro_treeview_keypress(wimp_key *key); + +static void ro_treeview_set_window_extent(ro_treeview *tv, + int width, int height); + +static const struct treeview_table ro_tree_callbacks = { + ro_treeview_redraw_request, + ro_treeview_resized, + ro_treeview_scroll_visible, + ro_treeview_get_window_dimensions +}; /** - * Initialise a sprite for use with Tinct + * Create a RISC OS GUI implementation of a treeview tree. + * + * \param window The window to create the tree in. + * \param *toolbar A toolbar to attach to the window. + * \param flags The treeview flags. * - * \param name the name of the sprite - * \param number the sprite cache number - * \return whether an error occurred during initialisation + * \return The RISC OS treeview pointer. */ -bool ro_gui_tree_initialise_sprite(const char *name, int number) -{ - char icon_name[12]; - const char *icon = icon_name; - os_error *error; - - snprintf(icon_name, sizeof(icon_name), "tr_%s", name); - - error = xosspriteop_select_sprite(osspriteop_USER_AREA, gui_sprites, - (osspriteop_id) icon, - (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; -} +ro_treeview *ro_treeview_create(wimp_w window, struct toolbar *toolbar, + unsigned int flags) +{ + ro_treeview *tv; + /* Claim memory for the treeview block, and create a 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) -{ - os_error *error; + tv = malloc(sizeof(ro_treeview)); + if (tv == NULL) + return NULL; - assert(tree); - assert(tree->handle); + tv->w = window; + tv->tb = toolbar; - if (tree->toolbar) - y += ro_gui_theme_toolbar_height(tree->toolbar); - 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); + tv->tree = tree_create(flags, &ro_tree_callbacks, tv); + if (tv->tree == NULL) { + free(tv); + return NULL; } -} + /* Set the tree redraw origin at a default 0,0 RO units. */ -/** - * 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(int x, int y, int width, int height) -{ - os_error *error; - int y0, y1; + tv->origin.x = 0; + tv->origin.y = 0; - /* stop the 16-bit co-ordinate system from causing redraw errors */ - y1 = ro_gui_tree_origin_y - y; - if (y1 < 0) - return; - y0 = y1 - height; - if (y0 > 16384) - return; - if (y0 < 0) - y0 = 0; - if (y1 > 16384) - y1 = 16384; + /* Set the tree size as 0,0 to indicate that we don't know. */ - 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, y0); - if (!error) - xos_plot(os_PLOT_TO, ro_gui_tree_origin_x + x + width, y1); - if (error) { - LOG(("xos_plot: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("MiscError", error->errmess); - return; - } -} + tv->size.x = 0; + tv->size.y = 0; + /* Set the tree window extent to 0,0, to indicate that we + * don't know. */ -/** - * 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 toolbar_height = 0; - struct node_element *url_element; - const struct bitmap *bitmap = NULL; - struct node_update *update; - const uint8_t *frame; - rufl_code code; - int x0, y0, x1, y1; - bool selected = false; - colour bg, c; - - assert(tree); - assert(element); - assert(element->parent); - - if (tree->toolbar) - toolbar_height = ro_gui_theme_toolbar_height(tree->toolbar); - - x0 = ro_gui_tree_origin_x + element->box.x; - x1 = x0 + element->box.width; - y1 = ro_gui_tree_origin_y - element->box.y; - y0 = y1 - element->box.height; - if (&element->parent->data == element) - if (element->parent->selected) - selected = true; - - - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_SPRITE: - assert(element->sprite); - - ro_gui_tree_icon.flags = wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED; - if (selected) - ro_gui_tree_icon.flags |= wimp_ICON_SELECTED; - ro_gui_tree_icon.extent.x0 = tree->offset_x + element->box.x; - ro_gui_tree_icon.extent.y1 = -tree->offset_y - element->box.y - - toolbar_height; - ro_gui_tree_icon.extent.x1 = ro_gui_tree_icon.extent.x0 + - NODE_INSTEP; - ro_gui_tree_icon.extent.y0 = -tree->offset_y - element->box.y - - element->box.height - toolbar_height; - 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); - } - 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); - } - x0 += NODE_INSTEP; + tv->extent.x = 0; + tv->extent.y = 0; - /* fall through */ - case NODE_ELEMENT_TEXT: - assert(element->text); + /* Set that there is no drag opperation at the moment */ - if (element == tree->editing) - return; + tv->drag = false; - if (ro_gui_tree_icon.flags & wimp_ICON_SELECTED) - ro_gui_tree_icon.flags |= wimp_ICON_FILLED; - if (selected) { - error = xcolourtrans_set_gcol((os_colour)0x00000000, 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, x0, y0); - if (!error) - error = xos_plot(os_PLOT_RECTANGLE | os_PLOT_TO, x1-1, y1-1); - if (error) { - LOG(("xos_plot: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("MiscError", error->errmess); - return; - } - bg = 0x0000000; - c = 0x00eeeeee; - } else { - bg = 0x00ffffff; - c = 0x00000000; - } - error = xcolourtrans_set_font_colours(font_CURRENT, - bg << 8, c << 8, 14, 0, 0, 0); - if (error) { - LOG(("xcolourtrans_set_font_colours: 0x%x: %s", - error->errnum, error->errmess)); - return; - } - code = rufl_paint(ro_gui_desktop_font_family, ro_gui_desktop_font_style, - ro_gui_desktop_font_size, - element->text, strlen(element->text), - x0 + 8, y0 + 10, - rufl_BLEND_FONT); - if (code != rufl_OK) { - if (code == rufl_FONT_MANAGER_ERROR) - LOG(("rufl_paint: rufl_FONT_MANAGER_ERROR: 0x%x: %s", - rufl_fm_error->errnum, - rufl_fm_error->errmess)); - else - LOG(("rufl_paint: 0x%x", code)); - } - break; - case NODE_ELEMENT_THUMBNAIL: - url_element = tree_find_element(element->parent, TREE_ELEMENT_URL); - if (url_element) - bitmap = urldb_get_thumbnail(url_element->text); - if (bitmap) { - frame = bitmap_get_buffer((struct bitmap *) bitmap); - if (!frame) - urldb_set_thumbnail(url_element->text, NULL); - if ((!frame) || (element->box.width == 0)) { - update = calloc(sizeof(struct node_update), 1); - if (!update) - return; - update->tree = tree; - update->node = element->parent; - schedule(0, tree_handle_node_changed_callback, - update); - return; - } - image_redraw(bitmap->sprite_area, - ro_gui_tree_origin_x + element->box.x + 2, - ro_gui_tree_origin_y - element->box.y, - bitmap->width, bitmap->height, - bitmap->width, bitmap->height, - 0xffffff, - false, false, false, - IMAGE_PLOT_TINCT_OPAQUE); - if (!tree->no_furniture) { - tree_draw_line(element->box.x, - element->box.y, - element->box.width - 1, - 0); - tree_draw_line(element->box.x, - element->box.y, - 0, - element->box.height - 3); - tree_draw_line(element->box.x, - element->box.y + element->box.height - 3, - element->box.width - 1, - 0); - tree_draw_line(element->box.x + element->box.width - 1, - element->box.y, - 0, - element->box.height - 3); - } - } - break; - } -} + /* Register wimp events to handle the supplied window. */ + ro_gui_wimp_event_register_redraw_window(tv->w, ro_treeview_redraw); + ro_gui_wimp_event_register_open_window(tv->w, ro_treeview_open); + ro_gui_wimp_event_register_mouse_click(tv->w, ro_treeview_mouse_click); + ro_gui_wimp_event_register_keypress(tv->w, ro_treeview_keypress); + ro_gui_wimp_event_set_user_data(tv->w, tv); -void tree_handle_node_changed_callback(void *p) -{ - struct node_update *update = p; + /* \todo Register wimp events to handle the supplied toolbar? */ - tree_handle_node_changed(update->tree, update->node, true, false); - free(update); + return tv; } - /** - * Draws an elements expansion icon + * Delete a RISC OS GUI implementation of a treeview tree. The window is + * *not* destroyed -- this must be done by the caller. * - * \param tree the tree to draw the expansion for - * \param element the element to draw the expansion for + * \param tv The RISC OS treeview to delete. */ -void tree_draw_node_expansion(struct tree *tree, struct node *node) -{ - unsigned int type; - assert(tree); - assert(node); +void ro_treeview_destroy(ro_treeview *tv) +{ + ro_gui_wimp_event_finalise(tv->w); - 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); + tree_delete(tv->tree); - } + free(tv); } - /** - * Sets the origin variables to the correct values for a specified tree + * Change the redraw origin of a treeview tree in RISC OS graphics units. * - * \param tree the tree to set the origin for + * \param *tv The ro_treeview object to update. + * \param x The X position, in terms of the RO window work area. + * \param y The Y position, in terms of the RO window work area. + * + * \todo -- this probably needs a rework. */ -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); +void ro_treeview_set_origin(ro_treeview *tv, int x, int y) +{ + if (tv != NULL) { + tv->origin.x = x; + tv->origin.y = y; + + /* Assuming that we know how big the tree currently is, then + * adjust the window work area extent to match. If we don't, + * then presumably the tree isn't in an open window yet and + * a subsequent Open Window Event should pick it up. + */ + + if (tv->size.x != 0 && tv->size.y != 0) + ro_treeview_set_window_extent(tv, + tv->origin.x + tv->size.x, + tv->origin.y + tv->size.y); } - - 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; - if (tree->toolbar) - ro_gui_tree_origin_y -= ro_gui_theme_toolbar_height(tree->toolbar); } - /** - * Recalculates the dimensions of a node element. + * Return details of the tree block associated with an ro_treeview object. * - * \param element the element to recalculate + * \param *tv The ro_treeview object of interest. + * \return A pointer to the associated tree block. */ -void tree_recalculate_node_element(struct node_element *element) + +struct tree *ro_treeview_get_tree(ro_treeview *tv) { - const struct bitmap *bitmap = NULL; - struct node_element *url_element; - rufl_code code; - - assert(element); - - switch (element->type) { - case NODE_ELEMENT_TEXT_PLUS_SPRITE: - assert(element->sprite); - case NODE_ELEMENT_TEXT: - assert(element->text); - code = rufl_width(ro_gui_desktop_font_family, ro_gui_desktop_font_style, - ro_gui_desktop_font_size, - element->text, strlen(element->text), - &element->box.width); - if (code != rufl_OK) { - if (code == rufl_FONT_MANAGER_ERROR) - LOG(("rufl_width: rufl_FONT_MANAGER_ERROR: 0x%x: %s", - rufl_fm_error->errnum, - rufl_fm_error->errmess)); - else - LOG(("rufl_width: 0x%x", code)); - } - 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_THUMBNAIL: - url_element = tree_find_element(element->parent, TREE_ELEMENT_URL); - if (url_element) - bitmap = urldb_get_thumbnail(url_element->text); - if (bitmap) { -/* if ((bitmap->width == 0) && (bitmap->height == 0)) - frame = bitmap_get_buffer(bitmap); - element->box.width = bitmap->width * 2 + 2; - element->box.height = bitmap->height * 2 + 4; -*/ element->box.width = THUMBNAIL_WIDTH * 2 + 2; - element->box.height = THUMBNAIL_HEIGHT * 2 + 4; - } else { - element->box.width = 0; - element->box.height = 0; - } - } + return (tv != NULL) ? (tv->tree) : (NULL); } - /** - * Sets a node element as having a specific sprite. + * Return details of the RISC OS window handle associated with an + * ro_treeview object. * - * \param node the node to update - * \param sprite the sprite to use - * \param selected the expanded sprite name to use + * \param *tv The ro_treeview object of interest. + * \return The associated RISC OS window handle. */ -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded) + +wimp_w ro_treeview_get_window(ro_treeview *tv) { - assert(node); - assert(sprite); - assert(expanded); - - 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); + return (tv != NULL) ? (tv->w) : (NULL); } - /** - * Sets a node element as having a folder sprite + * Return an indication of whether the supplied treeview object contains a + * selection. * - * \param node the node to update + * \param *tv The ro_treeview object of interest. + * \return true if there is a selection in the tree; else false. */ -void tree_set_node_sprite_folder(struct node *node) -{ - assert(node->folder); - tree_set_node_sprite(node, "small_dir", "small_diro"); +bool ro_treeview_has_selection(ro_treeview *tv) +{ + if (tv != NULL) + return tree_node_has_selection(tree_get_root(tv->tree)); + else + return false; } - /** - * Updates the node details for a URL node. - * The internal node dimensions are not updated. + * Callback to force a redraw of part of the treeview window. * - * \param node the node to update - * \param url the URL - * \param data the data the node is linked to, or NULL for unlinked data + * \param x Min X Coordinate of area to be redrawn. + * \param y Min Y Coordinate of area to be redrawn. + * \param width Width of area to be redrawn. + * \param height Height of area to be redrawn. + * \param pw The treeview object to be redrawn. */ -void tree_update_URL_node(struct node *node, - const char *url, const struct url_data *data) + +void ro_treeview_redraw_request(int x, int y, int width, int height, + void *pw) { - struct node_element *element; - char buffer[256]; + if (pw != NULL) { + ro_treeview *tv = (ro_treeview *) pw; + os_error *error; + wimp_draw update; + osbool more; - assert(node); + /* The scale can't be changed; it's always 1:1. */ - element = tree_find_element(node, TREE_ELEMENT_URL); - if (!element) - return; - if (data) { - /* node is linked, update */ - assert(!node->editable); - if (!data->title) - urldb_set_url_title(url, url); + plot = ro_plotters; + ro_plot_set_scale(1.0); - if (!data->title) - return; + update.w = tv->w; + update.box.x0 = (2 * x) + tv->origin.x; + update.box.y0 = (-2 * (y + height)) + tv->origin.y; + update.box.x1 = (2 * (x + width)) + tv->origin.x; + update.box.y1 = (-2 * y) + tv->origin.y; - node->data.text = data->title; - } else { - /* node is not linked, find data */ - assert(node->editable); - data = urldb_get_url_data(element->text); - if (!data) + error = xwimp_update_window(&update, &more); + if (error) { + LOG(("xwimp_update_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); return; - } - - if (element) { - sprintf(buffer, "small_%.3x", ro_content_filetype_from_type(data->type)); - 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_LAST_VISIT); - if (element) { - snprintf(buffer, 256, messages_get("TreeLast"), - (data->last_visit > 0) ? - ctime((time_t *)&data->last_visit) : - messages_get("TreeUnknown")); - if (data->last_visit > 0) - buffer[strlen(buffer) - 1] = '\0'; - free((void *)element->text); - element->text = strdup(buffer); - } - - element = tree_find_element(node, TREE_ELEMENT_VISITS); - if (element) { - snprintf(buffer, 256, messages_get("TreeVisits"), - data->visits); - free((void *)element->text); - element->text = strdup(buffer); + } + ro_treeview_redraw_loop(&update, tv, more); } } - - /** - * Updates the tree owner following a tree resize + * Pass RISC OS redraw events on to the treeview widget. * - * \param tree the tree to update the owner of + * \param *redraw Pointer to Redraw Event block. */ -void tree_resized(struct tree *tree) + +void ro_treeview_redraw(wimp_draw *redraw) { - os_error *error; - wimp_window_state state; + osbool more; + os_error *error; + ro_treeview *tv; + + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(redraw->w); + if (tv == NULL) { + LOG(("NULL treeview block for window: 0x%x", + (unsigned int) redraw->w)); + /* Don't return, as not servicing redraw events isn't a good + * idea. The following code must handle (tv == NULL) + * gracefully while clearing the redraw queue. + */ + } - assert(tree->handle); + /* The scale can't be changed; it's always 1:1. */ + plot = ro_plotters; + ro_plot_set_scale(1.0); - state.w = (wimp_w)tree->handle; - error = xwimp_get_window_state(&state); + error = xwimp_redraw_window(redraw, &more); if (error) { - LOG(("xwimp_get_window_state: 0x%x: %s", + LOG(("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } - if (state.flags & wimp_WINDOW_OPEN) - ro_gui_tree_open(PTR_WIMP_OPEN(&state)); -} + ro_treeview_redraw_loop(redraw, tv, more); +} /** - * Redraws a tree window + * Redraw a treeview window, once the initial readraw block has been collected. * - * \param redraw the area to redraw - * \param tree the tree to redraw + * /param *redraw Pointer to redraw block. + * /param *tv The treeview object being redrawn. + * /param more Flag to show if more actions are required. */ -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; - - tree = (struct tree *)ro_gui_wimp_event_get_user_data(redraw->w); - assert(tree); +void ro_treeview_redraw_loop(wimp_draw *redraw, ro_treeview *tv, osbool more) +{ + os_error *error; - 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; - if (tree->toolbar) - origin_y -= ro_gui_theme_toolbar_height(tree->toolbar); - 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); + ro_plot_origin_x = redraw->box.x0 - redraw->xscroll; + ro_plot_origin_y = redraw->box.y1 - redraw->yscroll; + + if (tv != NULL && tv->tree != NULL) { + tree_draw(tv->tree, tv->origin.x/2, -(tv->origin.y/2), + (redraw->clip.x0 + -(ro_plot_origin_x+tv->origin.x))/2, + ((ro_plot_origin_y+tv->origin.y) + -redraw->clip.y1)/2, + (redraw->clip.x1 - redraw->clip.x0)/2, + (redraw->clip.y1 - redraw->clip.y0)/2); + } + + error = xwimp_get_rectangle(redraw, &more); + if (error) { + LOG(("xwimp_redraw_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } } } - /** - * Handles a mouse click for a tree + * Callback to notify us of a new overall tree size. * - * \param pointer the pointer state - * \param tree the tree to handle a click for - * \return whether the click was handled + * \param tree The tree being resized. + * \param width The new width of the window. + * \param height The new height of the window. + * \param *pw The treeview object to be resized. */ -bool ro_gui_tree_click(wimp_pointer *pointer, struct tree *tree) -{ - bool furniture; - struct node *node; - struct node *last; - 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); +void ro_treeview_resized(struct tree *tree, int width, int height, + void *pw) +{ + if (pw != NULL) { + ro_treeview *tv = (ro_treeview *) pw; - /* 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); + /* Store the width and height in terms of RISC OS work area. */ - /* handle a menu click */ - if (pointer->buttons == wimp_CLICK_MENU) { - if ((!element) || (!tree->root->child) || - (tree_has_selection(tree->root->child))) - return true; + tv->size.x = width * 2; + tv->size.y = -(height * 2); - node = element->parent; - tree->temp_selection = node; - node->selected = true; - tree_handle_node_element_changed(tree, &node->data); - return true; + /* Resize the window. */ + ro_treeview_set_window_extent(tv, tv->size.x, tv->size.y); } +} - /* no item either means cancel selection on (select) click or a drag */ - if (!element) { - if (tree->single_selection) { - tree_set_node_selected(tree, tree->root->child, false); - return true; - } - 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; - if (tree->toolbar) - scroll.pause_zone_sizes.y1 += - ro_gui_theme_toolbar_height(tree->toolbar); - 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)); +/** + * Callback to request that a section of the tree is scrolled into view. + * + * \param y The Y coordinate of top of the area in NS units. + * \param height The height of the area in NS units. + * \param *pw The treeview object affected. + */ +void ro_treeview_scroll_visible(int y, int height, void *pw) +{ + if (pw != NULL) { + ro_treeview *tv = (ro_treeview *) pw; + os_error *error; + wimp_window_state state; + int visible_t, visible_b; + int request_t, request_b; + + state.w = tv->w; + 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; } - return true; - } - node = element->parent; - - /* 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); - - /* find the last child node if expanded */ - last = node; - if ((last->child) && (last->expanded)) { - last = last->child; - while ((last->next) || ((last->child) && (last->expanded))) { - if (last->next) - last = last->next; - else - last = last->child; - } - } - /* scroll to the bottom element then back to the top */ - element = &last->data; - if (last->expanded) - for (; element->next; element = element->next); - ro_gui_tree_scroll_visible(tree, element); - ro_gui_tree_scroll_visible(tree, &node->data); - return true; - } + /* Work out top and bottom of both the currently visible and + * the required areas, in terms of the RO work area. + */ - /* no use for any other furniture click */ - if (furniture) - return true; - - /* single/double alt+click starts editing */ - if ((node->editable) && (!tree->editing) && - ((element->data == TREE_ELEMENT_URL) || - (element->data == TREE_ELEMENT_TITLE)) && - ((pointer->buttons == wimp_CLICK_SELECT) || - (pointer->buttons == (wimp_CLICK_SELECT << 8)))) { - xosbyte1(osbyte_SCAN_KEYBOARD, 2 ^ 0x80, 0, &alt_pressed); - if (alt_pressed == 0xff) { - ro_gui_tree_start_edit(tree, element, pointer); - return true; - } - } + visible_t = state.yscroll; + visible_b = state.yscroll + - (state.visible.y1 - state.visible.y0); - /* double click starts launches the leaf */ - if ((pointer->buttons == wimp_CLICK_SELECT) || - (pointer->buttons == wimp_CLICK_ADJUST)) { - if (!ro_gui_tree_launch_node(tree, node)) - return false; - if (pointer->buttons == wimp_CLICK_ADJUST) - ro_gui_dialog_close((wimp_w)tree->handle); - return true; - } + request_t = -(2 * y);// - tv->origin.y; + request_b = -(2 * (y + height));// - tv->origin.y; - /* single click (select) cancels current selection and selects item */ - if ((pointer->buttons == (wimp_CLICK_SELECT << 8)) || - ((pointer->buttons == (wimp_CLICK_ADJUST << 8)) && - (tree->single_selection))) { - 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; - } + /* If the area is outside the visible window, then scroll it + * in to view. + */ - /* 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; - } + if (request_t > visible_t || request_b < visible_b) { + if (request_t > visible_t) { + state.yscroll = request_t; + } else if (request_b < visible_b) { + state.yscroll = request_b + tv->origin.y + + (state.visible.y1 - state.visible.y0); - /* drag starts a drag operation */ - if ((!tree->editing) && ((pointer->buttons == (wimp_CLICK_SELECT << 4)) || - (pointer->buttons == (wimp_CLICK_ADJUST << 4)))) { - if (tree->no_drag) - return true; + /* If the required area is bigger than the + * visible extent, then align to the top and + * let the bottom disappear out of view. + */ - if (!node->selected) { - node->selected = true; - tree_handle_node_element_changed(tree, &node->data); - } + if (state.yscroll < request_t) + state.yscroll = request_t; + } - scroll.w = (wimp_w)tree->handle; - scroll.pause_zone_sizes.y0 = 80; - scroll.pause_zone_sizes.y1 = 80; - if (tree->toolbar) - scroll.pause_zone_sizes.y1 += - ro_gui_theme_toolbar_height(tree->toolbar); - 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 { - /* small_xxx -> file_xxx */ - sprintf(ro_gui_tree_drag_name, "file_%s", - node->data.sprite->name + 6); - if (!ro_gui_wimp_sprite_exists(ro_gui_tree_drag_name)) - sprintf(ro_gui_tree_drag_name, "file_xxx"); + error = xwimp_open_window((wimp_open *) &state); + if (error) { + LOG(("xwimp_open_window: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; } - } 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 + * Callback to return the tree window dimensions to the treeview system. * - * \param tree the tree to handle the event for + * \param *width Return the window width. + * \param *height Return the window height. + * \param *pw The treeview object to use. */ -void ro_gui_tree_menu_closed(struct tree *tree) + +void ro_treeview_get_window_dimensions(int *width, int *height, + void *pw) { - 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; - ro_gui_menu_prepare_action((wimp_w)tree->handle, TREE_SELECTION, false); - ro_gui_menu_prepare_action((wimp_w)tree->handle, TREE_EXPAND_ALL, false); + if (pw != NULL && (width != NULL || height != NULL)) { + ro_treeview *tv = (ro_treeview *) pw; + os_error *error; + wimp_window_state state; + + state.w = tv->w; + error = xwimp_get_window_state(&state); + if (error) { + LOG(("xwimp_get_window_info_header_only: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + + if (width != NULL) + *width = (state.visible.x1 - state.visible.x0) / 2; + + if (height != NULL) + *height = (state.visible.y1 - state.visible.y0) / 2; } } - /** - * Respond to a mouse click for a tree (hotlist or history) toolbar + * Resize the RISC OS window extent of a treeview. * - * \param pointer the pointer state + * \param *tv The RISC OS treeview object to resize. + * \param width The new width of the work area, in RO units. + * \param height The new height of the work area, in RO units. */ -bool ro_gui_tree_toolbar_click(wimp_pointer* pointer) + +void ro_treeview_set_window_extent(ro_treeview *tv, int width, int height) { - struct node *node; + if (tv != NULL) { + os_error *error; + os_box extent; + wimp_window_state state; + int new_x, new_y; + int visible_x, visible_y; + int new_x_scroll, new_y_scroll; - struct toolbar *toolbar = - (struct toolbar *)ro_gui_wimp_event_get_user_data(pointer->w); - assert(toolbar); - struct tree *tree = - (struct tree *)ro_gui_wimp_event_get_user_data(toolbar->parent_handle); - assert(tree); + /* Calculate the new window extents, in RISC OS units. */ - ro_gui_tree_stop_edit(tree); + new_x = width + tv->origin.x; + new_y = height + tv->origin.y; - if (pointer->buttons == wimp_CLICK_MENU) { - ro_gui_menu_create(tree_toolbar_menu, pointer->pos.x, - pointer->pos.y, (wimp_w)tree->handle); - return true; - } + /* Get details of the existing window, and start to sanity + * check the new extents. + */ - if (tree->toolbar->editor) { - ro_gui_theme_toolbar_editor_click(tree->toolbar, pointer); - return true; - } + state.w = tv->w; + 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; + } - switch (pointer->i) { - case ICON_TOOLBAR_CREATE: - node = tree_create_folder_node(tree->root, - messages_get("TreeNewFolder")); - tree_redraw_area(tree, node->box.x - NODE_INSTEP, - 0, NODE_INSTEP, 16384); - tree_handle_node_changed(tree, node, false, true); - ro_gui_tree_start_edit(tree, &node->data, NULL); - break; - case ICON_TOOLBAR_OPEN: - tree_handle_expansion(tree, tree->root, - (pointer->buttons == wimp_CLICK_SELECT), - true, false); - break; - case ICON_TOOLBAR_EXPAND: - tree_handle_expansion(tree, tree->root, - (pointer->buttons == wimp_CLICK_SELECT), - false, true); - break; - case ICON_TOOLBAR_DELETE: - ro_gui_menu_handle_action((wimp_w)tree->handle, - TREE_SELECTION_DELETE, false); - break; - case ICON_TOOLBAR_LAUNCH: - ro_gui_menu_handle_action((wimp_w)tree->handle, - TREE_SELECTION_LAUNCH, false); - break; - } - return true; -} + /* If the extent is smaller than the current visible area, + * then extend it so that it matches the visible area. + */ + if (new_x < (state.visible.x1 - state.visible.x0)) + new_x = state.visible.x1 - state.visible.x0; -/** - * 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; - struct node *parent; - int toolbar_height = 0; - - assert(tree); - assert(element); - - if (tree->editing) - ro_gui_tree_stop_edit(tree); - if (tree->toolbar) - toolbar_height = ro_gui_theme_toolbar_height(tree->toolbar); - - 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); - } - } + if (new_y > (state.visible.y0 - state.visible.y1)) + new_y = state.visible.y0 - state.visible.y1; - tree->editing = element; - 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 - toolbar_height - - element->box.y; - ro_gui_tree_edit_icon.icon.extent.y0 = -tree->offset_y - toolbar_height - - 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 = - (char *) element->text; - 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)); + /* Calculate the maximum visible coordinates of the existing + * window. + */ - tree->textarea_handle = ro_textarea_create((wimp_w)tree->handle, - (wimp_i)tree->edit_handle, 0, ro_gui_desktop_font_family, - ro_gui_desktop_font_size, - ro_gui_desktop_font_style); - if (!tree->textarea_handle) { - ro_gui_tree_stop_edit(tree); - return; - } - ro_textarea_set_text(tree->textarea_handle, element->text); - if (pointer) - ro_textarea_set_caret_xy(tree->textarea_handle, - pointer->pos.x, pointer->pos.y); - else - ro_textarea_set_caret(tree->textarea_handle, - strlen(element->text)); + visible_x = state.xscroll + + (state.visible.x1 - state.visible.x0); + visible_y = state.yscroll + + (state.visible.y0 - state.visible.y1); - tree_handle_node_element_changed(tree, element); - ro_gui_tree_scroll_visible(tree, element); -} + /* If the window is currently open, and the exising visible + * area is bigger than the new extent, then we need to reopen + * the window in an appropriare position before setting the + * new extent. + */ + if ((state.flags & wimp_WINDOW_OPEN) && + (visible_x > new_x || visible_y < new_y)) { + new_x_scroll = state.xscroll; + new_y_scroll = state.yscroll; -/** - * 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; + if (visible_x > new_x) + new_x_scroll = new_x - (state.visible.x1 + - state.visible.x0); - assert(tree); + if (visible_y < new_y) + new_y_scroll = new_y - (state.visible.y0 + - state.visible.y1); - if (!tree->editing) return; + if (new_x_scroll < 0) { + state.visible.x1 -= new_x_scroll; + state.xscroll = 0; + } else { + state.xscroll = new_x_scroll; + } - if (tree->textarea_handle) { - ro_textarea_destroy(tree->textarea_handle); - tree->textarea_handle = 0; - } - 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); -} + if (new_y_scroll > 0) { + state.visible.y0 += new_y_scroll; + state.yscroll = 0; + } else { + state.yscroll = new_y_scroll; + } + error = xwimp_open_window((wimp_open *) &state); + if (error) { + LOG(("xwimp_get_window_state: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } -/** - * 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; - int toolbar_height = 0; + /* \todo -- Not sure if we need to reattach the + * toolbar here: the nested wimp seems to take care + * of it for us? + */ + } - assert(element); + /* Now that the new extent fits into the visible window, we + * can resize the work area. If we succeed, the values are + * recorded to save having to ask the Wimp for them + * each time. + */ - if (tree->toolbar) - toolbar_height = ro_gui_theme_toolbar_height(tree->toolbar); + extent.x0 = 0; + extent.y0 = new_y; + extent.x1 = new_x; + extent.y1 = 0; - 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 - toolbar_height; - - state.yscroll = state.visible.y1 - state.visible.y0 - tree->offset_y - - toolbar_height - 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 - toolbar_height - - (element->box.y + element->box.height); - ro_gui_tree_open(PTR_WIMP_OPEN(&state)); -} + error = xwimp_set_extent(tv->w, &extent); + if (error) { + LOG(("xwimp_set_extent: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("WimpError", error->errmess); + return; + } + tv->extent.x = new_x; + tv->extent.y = new_y; + } +} /** - * Shows the a tree window. + * Handle RISC OS Window Open events for a treeview window. + * + * \param *open Pointer to the Window Open Event block. */ -void ro_gui_tree_show(struct tree *tree) + +static void ro_treeview_open(wimp_open *open) { - struct toolbar *toolbar; - - /* we may have failed to initialise */ - if (!tree) return; - toolbar = tree->toolbar; - - /* handle first time opening */ - if (!ro_gui_dialog_open_top((wimp_w)tree->handle, toolbar, 600, 800)) { - ro_gui_tree_stop_edit(tree); - if (tree->root->child) { - tree_set_node_selected(tree, tree->root, false); - tree_handle_node_changed(tree, tree->root, - false, true); - } + ro_treeview *tv; + os_error *error; + os_box extent; + int width, height; + + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(open->w); + if (tv == NULL) { + LOG(("NULL treeview block for window: ox%x", + (unsigned int) open->w)); + return; } - /* set the caret position */ - xwimp_set_caret_position((wimp_w)tree->handle, -1, -100, -100, 32, -1); -} + /* Calculate the window work area. It must be at least the same as + * the current visible area of the window, and needs to contain the + * tree as defined by size.x + offset.x and size.y + offset.y (note + * that the offset.y should be set to cover any toolbar, so we can + * ignore the size of that). + */ + width = open->visible.x1 - open->visible.x0; + height = open->visible.y0 - open->visible.y1; -/** - * Handles a window open request - * - * \param open the window state - */ -void ro_gui_tree_open(wimp_open *open) -{ - struct tree *tree; - os_error *error; - int width; - int height; - int toolbar_height = 0; - bool vscroll; + if (tv->size.x != 0 && width < (tv->origin.x + tv->size.x)) + width = (tv->origin.x + tv->size.x); - tree = (struct tree *)ro_gui_wimp_event_get_user_data(open->w); + if (tv->size.y != 0 && height > (tv->size.y + tv->origin.y)) + height = (tv->size.y + tv->origin.y); - if (!tree) - return; - if (tree->toolbar) - toolbar_height = ro_gui_theme_toolbar_height(tree->toolbar); + if (width != tv->extent.x || height != tv->extent.y) { + extent.x0 = 0; + extent.y0 = height; + extent.x1 = width; + extent.y1 = 0; - 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 + toolbar_height + tree->height)) - height = tree->offset_y + toolbar_height + 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); + error = xwimp_set_extent(tv->w, &extent); if (error) { LOG(("xwimp_set_extent: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); + return; } - /* hide the scroll bar? */ - if ((tree->no_vscroll) && (height != tree->window_height)) { - vscroll = (tree->height > height); - if (ro_gui_wimp_check_window_furniture(open->w, - wimp_WINDOW_VSCROLL) != vscroll) { - ro_gui_wimp_update_window_furniture(open->w, - 0, wimp_WINDOW_VSCROLL); - if (vscroll) - open->visible.x1 -= ro_get_vscroll_width(open->w); - else - open->visible.x1 += ro_get_vscroll_width(open->w); - } - } - - tree->window_width = width; - tree->window_height = height; + tv->extent.x = width; + tv->extent.y = height; } + /* \todo -- Might need to add vertical scrollbar hiding back in here? */ + error = xwimp_open_window(open); if (error) { LOG(("xwimp_open_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); } - if (tree->toolbar) - ro_gui_theme_process_toolbar(tree->toolbar, -1); - ro_gui_menu_prepare_action((wimp_w)tree->handle, TREE_SELECTION, false); - ro_gui_menu_prepare_action((wimp_w)tree->handle, TREE_EXPAND_ALL, false); + + if (tv->tb) + ro_gui_theme_process_toolbar(tv->tb, -1); } /** - * Handles a keypress for a tree + * Pass RISC OS Mouse Click events on to the treeview widget. * - * \param key the key pressed - * \param tree the tree to handle a keypress for - * \return whether the key was processed + * \param *pointer Pointer to the Mouse Click Event block. + * \return Return true if click handled; else false. */ -bool ro_gui_tree_keypress(wimp_key *key) + +static bool ro_treeview_mouse_click(wimp_pointer *pointer) { - wimp_window_state state; - int y; - char *new_string; - struct tree *tree; - int strlen; - os_error *error; - - tree = (struct tree *)ro_gui_wimp_event_get_user_data(key->w); - if (!tree) + os_error *error; + ro_treeview *tv; + wimp_window_state state; + int xpos, ypos; + browser_mouse_state mouse; + + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(pointer->w); + if (tv == NULL) { + LOG(("NULL treeview block for window: 0x%x", + (unsigned int) pointer->w)); return false; - - /* Handle basic keys - */ - switch (key->c) { - case 1: /* CTRL+A */ - ro_gui_menu_handle_action((wimp_w)tree->handle, - TREE_SELECT_ALL, false); - return true; - case 24: /* CTRL+X */ - ro_gui_menu_handle_action((wimp_w)tree->handle, - TREE_SELECTION_DELETE, false); - return true; - case 26: /* CTRL+Z */ - ro_gui_menu_handle_action((wimp_w)tree->handle, - TREE_CLEAR_SELECTION, false); - return true; - case wimp_KEY_RETURN: - if ((tree->editing) && (tree->textarea_handle)) { - strlen = ro_textarea_get_text( - tree->textarea_handle, - NULL, 0); - if (strlen == -1) { - ro_gui_tree_stop_edit(tree); - return true; - } - new_string = malloc(strlen); - if (!new_string) { - ro_gui_tree_stop_edit(tree); - LOG(("No memory for malloc()")); - warn_user("NoMemory", 0); - return true; - } - ro_textarea_get_text(tree->textarea_handle, - new_string, strlen); - free((void *)tree->editing->text); - 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_ESCAPE: - if (tree->editing) { - ro_gui_tree_stop_edit(tree); - } else { - /* \todo cancel drags etc. */ - } - return true; - - case IS_WIMP_KEY | wimp_KEY_UP: - case IS_WIMP_KEY | wimp_KEY_DOWN: - case IS_WIMP_KEY | wimp_KEY_PAGE_UP: - case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN: - case IS_WIMP_KEY | wimp_KEY_HOME: - case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP: - case IS_WIMP_KEY | wimp_KEY_END: - case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN: - break; - default: - return false; } - /* keyboard scrolling */ - state.w = (wimp_w)tree->handle; + state.w = tv->w; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); - return true; + warn_user("WimpError", error->errmess); + return false; } - y = state.visible.y1 - state.visible.y0 - TREE_TEXT_HEIGHT; - if (tree->toolbar) - y -= ro_gui_theme_toolbar_full_height(tree->toolbar); + /* Convert the returned mouse coordinates into NetSurf's internal + * units. + */ - switch (key->c) { - case IS_WIMP_KEY | wimp_KEY_UP: -/* if ((state.yscroll % TREE_TEXT_HEIGHT) != 0) - state.yscroll -= (state.yscroll % TREE_TEXT_HEIGHT); - else -*/ state.yscroll += TREE_TEXT_HEIGHT; - break; - case IS_WIMP_KEY | wimp_KEY_DOWN: -/* if (((state.yscroll + y) % TREE_TEXT_HEIGHT) != 0) - state.yscroll -= ((state.yscroll + y) % TREE_TEXT_HEIGHT); - else -*/ state.yscroll -= TREE_TEXT_HEIGHT; - break; - case IS_WIMP_KEY | wimp_KEY_PAGE_UP: - state.yscroll += y; - break; - case IS_WIMP_KEY | wimp_KEY_PAGE_DOWN: - state.yscroll -= y; - break; - case IS_WIMP_KEY | wimp_KEY_HOME: - case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_UP: - state.yscroll = 0x10000000; - break; - case IS_WIMP_KEY | wimp_KEY_END: - case IS_WIMP_KEY | wimp_KEY_CONTROL | wimp_KEY_DOWN: - state.yscroll = -0x10000000; - break; - } + xpos = ((pointer->pos.x - state.visible.x0) + + state.xscroll - tv->origin.x) / 2; + ypos = ((state.visible.y1 - pointer->pos.y) - + state.yscroll + tv->origin.y) / 2; - error = xwimp_open_window(PTR_WIMP_OPEN(&state)); - if (error) { - LOG(("xwimp_open_window: 0x%x: %s", - error->errnum, error->errmess)); + /* Start to process the mouse click. + * + * Select and Adjust are processed normally. To get filer-like operation + * with selections, Menu clicks are passed to the treeview first as + * Select if there are no selected nodes in the tree. + */ + + mouse = 0; + + if (pointer->buttons == wimp_CLICK_MENU) { + if (!tree_node_has_selection(tree_get_root(tv->tree))) + mouse |= BROWSER_MOUSE_CLICK_1; + } else { + mouse = ro_gui_mouse_click_state(pointer->buttons, + wimp_BUTTON_DOUBLE_CLICK_DRAG); + + /* Record drag start */ + if (mouse & (BROWSER_MOUSE_DRAG_1 | BROWSER_MOUSE_DRAG_2)) { + tv->drag_start.x = xpos; + tv->drag_start.y = ypos; + tv->drag = true; + } + + if (mouse & (BROWSER_MOUSE_CLICK_1 | BROWSER_MOUSE_MOD_2)) + xwimp_set_caret_position(tv->w, -1, -100, -100, 32, -1); } + if (mouse != 0) + tree_mouse_action(tv->tree, mouse, xpos, ypos); + + /* We assume that the owning module will have attached a window menu + * to our parent window. If it hasn't, this call will quietly fail. + */ + + if (pointer->buttons == wimp_CLICK_MENU) + ro_gui_wimp_event_process_window_menu_click(pointer); + return true; } - /** - * Handles the completion of a selection drag (GUI_DRAG_TREE_SELECT) + * Track the mouse under Null Polls from the wimp, to support dragging. * - * \param drag the drag box information + * \param w Window handle currently under the mouse. + * \param *pointer Pointer to a Wimp Pointer block. */ -void ro_gui_tree_selection_drag_end(wimp_dragged *drag) + +void ro_treeview_mouse_at(wimp_w w, wimp_pointer *pointer) { - wimp_window_state state; - wimp_auto_scroll_info scroll; - os_error *error; - int x0, y0, x1, y1; - int toolbar_height = 0; - - if (ro_gui_tree_current_drag_tree->toolbar) - toolbar_height = ro_gui_theme_toolbar_height( - ro_gui_tree_current_drag_tree->toolbar); - - 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; + os_error *error; + ro_treeview *tv; + wimp_window_state state; + int xpos, ypos; + browser_mouse_state mouse; + + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(pointer->w); + if (tv == NULL) { + LOG(("NULL treeview block for window: 0x%x", + (unsigned int) pointer->w)); + return; + } + + state.w = tv->w; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", @@ -1437,177 +809,177 @@ void ro_gui_tree_selection_drag_end(wimp_dragged *drag) 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 - toolbar_height; - 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 - toolbar_height; - 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))); - ro_gui_menu_prepare_action((wimp_w)ro_gui_tree_current_drag_tree->handle, - TREE_SELECTION, false); - ro_gui_menu_prepare_action((wimp_w)ro_gui_tree_current_drag_tree->handle, - TREE_EXPAND_ALL, false); -} + /* Convert the returned mouse coordinates into NetSurf's internal + * units. + */ + xpos = ((pointer->pos.x - state.visible.x0) + + state.xscroll - tv->origin.x) / 2; + ypos = ((state.visible.y1 - pointer->pos.y) - + state.yscroll + tv->origin.y) / 2; -/** - * 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; + /* Start to process the mouse click. */ + + mouse = 0; + + if (!(pointer->buttons & (wimp_CLICK_MENU))) { + mouse = ro_gui_mouse_drag_state(pointer->buttons, + wimp_BUTTON_DOUBLE_CLICK_DRAG); + if (mouse != 0) + tree_mouse_action(tv->tree, mouse, xpos, ypos); + + /* Check if drag ended and tell core */ + if (tv->drag && !(mouse & BROWSER_MOUSE_DRAG_ON)) { + tree_drag_end(tv->tree, mouse, tv->drag_start.x, + tv->drag_start.y, xpos, ypos); + tv->drag = false; + } - 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; - if (tree->toolbar) - *tree_y -= ro_gui_theme_toolbar_height(tree->toolbar); } - /** - * Handles the completion of a move drag (GUI_DRAG_TREE_MOVE) + * Pass RISC OS keypress events on to the treeview widget. * - * \param drag the drag box information + * \param *key Pointer to the Key Pressed Event block. + * \return Return true if keypress handled; else false. */ -void ro_gui_tree_move_drag_end(wimp_dragged *drag) + +static bool ro_treeview_keypress(wimp_key *key) { - struct gui_window *g; - wimp_pointer pointer; - wimp_auto_scroll_info scroll; - os_error *error; - struct node *node; - struct node *single; - struct node_element *element; - 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; + ro_treeview *tv; + uint32_t c; + + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(key->w); + if (tv == NULL) { + LOG(("NULL treeview block for window: 0x%x", + (unsigned int) key->w)); + return false; } - if (pointer.w != (wimp_w)ro_gui_tree_current_drag_tree->handle) { - /* try to drop into a browser window */ - single = tree_get_selected_node(ro_gui_tree_current_drag_tree->root->child); - element = tree_find_element(single, TREE_ELEMENT_URL); - if (!element) - return; - if (single) { - /* \todo:send datasave for element */ - g = ro_gui_window_lookup(pointer.w); - if (g) - browser_window_go(g->bw, element->text, 0, true); - return; - } else { - /* \todo:update save.c to handle multiple concurrent saves */ + c = (uint32_t) key->c; + + if ((unsigned)c < 0x20 || (0x7f <= c && c <= 0x9f) || + (c & IS_WIMP_KEY)) { + /* Munge control keys into unused control chars */ + /* We can't map onto 1->26 (reserved for ctrl+<qwerty> + That leaves 27->31 and 128->159 */ + switch (c & ~IS_WIMP_KEY) { + case wimp_KEY_TAB: c = 9; break; + case wimp_KEY_SHIFT | wimp_KEY_TAB: c = 11; break; + + /* cursor movement keys */ + case wimp_KEY_HOME: + case wimp_KEY_CONTROL | wimp_KEY_LEFT: + c = KEY_LINE_START; + break; + case wimp_KEY_END: + if (os_version >= RISCOS5) + c = KEY_LINE_END; + else + c = KEY_DELETE_RIGHT; + break; + case wimp_KEY_CONTROL | wimp_KEY_RIGHT: c = KEY_LINE_END; break; + case wimp_KEY_CONTROL | wimp_KEY_UP: c = KEY_TEXT_START; break; + case wimp_KEY_CONTROL | wimp_KEY_DOWN: c = KEY_TEXT_END; break; + case wimp_KEY_SHIFT | wimp_KEY_LEFT: c = KEY_WORD_LEFT ; break; + case wimp_KEY_SHIFT | wimp_KEY_RIGHT: c = KEY_WORD_RIGHT; break; + case wimp_KEY_SHIFT | wimp_KEY_UP: c = KEY_PAGE_UP; break; + case wimp_KEY_SHIFT | wimp_KEY_DOWN: c = KEY_PAGE_DOWN; break; + case wimp_KEY_LEFT: c = KEY_LEFT; break; + case wimp_KEY_RIGHT: c = KEY_RIGHT; break; + case wimp_KEY_UP: c = KEY_UP; break; + case wimp_KEY_DOWN: c = KEY_DOWN; break; + + /* editing */ + case wimp_KEY_CONTROL | wimp_KEY_END: + c = KEY_DELETE_LINE_END; + break; + case wimp_KEY_DELETE: + if (ro_gui_ctrl_pressed()) + c = KEY_DELETE_LINE_START; + else if (os_version < RISCOS5) + c = KEY_DELETE_LEFT; + break; + + default: + break; } - return; } - /* internal drag */ - if (!ro_gui_tree_current_drag_tree->movable) - return; - 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); -} + if (!(c & IS_WIMP_KEY)) { + if (tree_keypress(tv->tree, c)) + return true; + } + return false; +} -/** - * Launches all selected nodes. +/** Respond to a mouse click on a treeview toolbar. * - * \param tree the tree to launch all selected nodes for + * \param pointer Pointer to the MouseClick Event block. + * \return true if the event was handled; else false. */ -void ro_gui_tree_launch_selected(struct tree *tree) + +bool ro_gui_treeview_toolbar_click(wimp_pointer *pointer) { - assert(tree); + struct toolbar *tb; + ro_treeview *tv; - if (tree->root->child) - ro_gui_tree_launch_selected_node(tree, tree->root->child, false); -} + tb = (struct toolbar *) ro_gui_wimp_event_get_user_data(pointer->w); + if (tb == NULL) { + LOG(("NULL toolbar block for window: 0x%x", + (unsigned int) pointer->w)); + return false; + } + tv = (ro_treeview *) ro_gui_wimp_event_get_user_data(tb->parent_handle); + if (tv == NULL) { + LOG(("NULL treeview block for parent window: 0x%x", + (unsigned int) tb->parent_handle)); + return false; + } + + /* \todo -- Handle menu clicks here... */ + + /* \todo -- Deal with the editor here... */ + + switch (pointer->i) { -/** - * Launches all selected nodes. - * - * \param node the node to launch all selected nodes for - */ -void ro_gui_tree_launch_selected_node(struct tree *tree, struct node *node, - bool all) -{ - for (; node; node = node->next) { - if (((node->selected) || (all)) && (!node->folder)) - ro_gui_tree_launch_node(tree, node); - if ((node->child) && ((node->expanded) || (node->selected) | (all))) - ro_gui_tree_launch_selected_node(tree, node->child, - (node->selected) | (all)); } -} + return true; +} /** - * Launches a node using all known methods. + * Update a treeview to use a new theme. * - * \param node the node to launch - * \return whether the node could be launched + * \param *tv Pointer to the treeview to update. */ -bool ro_gui_tree_launch_node(struct tree *tree, struct node *node) + +void ro_treeview_update_theme(ro_treeview *tv) { - struct node_element *element; + if (tv != NULL && tv->tb != NULL){ + /* \todo -- Check for toolbar editing here. */ - assert(node); + if (!ro_gui_theme_update_toolbar(NULL, tv->tb)) { + ro_gui_theme_destroy_toolbar(tv->tb); + tv->tb = NULL; + } - element = tree_find_element(node, TREE_ELEMENT_URL); - if (element) { - browser_window_create(element->text, NULL, 0, true, false); - return true; - } + /* \todo -- Check for toolbar editing here. */ - element = tree_find_element(node, TREE_ELEMENT_SSL); - if (element) { - ro_gui_cert_open(tree, node); - return true; - } + ro_gui_theme_attach_toolbar(tv->tb, tv->w); + ro_treeview_set_origin(tv, 0, + -(ro_gui_theme_toolbar_height(tv->tb))); - return false; + xwimp_force_redraw(tv->w, 0, tv->extent.y, tv->extent.x, 0); + } } -int ro_gui_tree_help(int x, int y) -{ - return -1; -} -void ro_gui_tree_update_theme(struct tree *tree) -{ +#if 0 if ((tree) && (tree->toolbar)) { if (tree->toolbar->editor) if (!ro_gui_theme_update_toolbar(NULL, tree->toolbar->editor)) @@ -1621,4 +993,70 @@ void ro_gui_tree_update_theme(struct tree *tree) tree_resized(tree); xwimp_force_redraw((wimp_w)tree->handle, 0, -16384, 16384, 16384); } +#endif + +/** + * Return a token identifying the interactive help message for a given cursor + * position. + * + * Currently this is inimplemented. + * + * \param *message_data Pointer to the Wimp's help message block. + * \return Token value (-1 indicates no help available). + */ + +int ro_treeview_get_help(help_full_message_request *message_data) +{ + return -1; +} + +/** + * Convert a content type into an icon name. + * + * \todo -- Currently we don't have any icons apart from the default. + * + * \param *buffer A buffer to return the icon name + * \param type The content type to return an icon name for. + */ + +void tree_icon_name_from_content_type(char *buffer, content_type type) +{ + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_SPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_DRAW + case CONTENT_DRAW: +#endif +#ifdef WITH_ARTWORKS + case CONTENT_ARTWORKS: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + sprintf(buffer, tree_content_icon_name); + break; + } } + diff --git a/riscos/treeview.h b/riscos/treeview.h index df718fc7a..7aa129e1b 100644 --- a/riscos/treeview.h +++ b/riscos/treeview.h @@ -1,5 +1,6 @@ /* * Copyright 2004 Richard Wilson <not_ginger_matt@users.sourceforge.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -24,38 +25,30 @@ #define _NETSURF_RISCOS_TREEVIEW_H_ #include <stdbool.h> -#include "oslib/osspriteop.h" -#include "oslib/wimp.h" +#include <oslib/help.h> +#include <oslib/wimp.h> + #include "desktop/tree.h" -#include "image/bitmap.h" -#define TREE_TEXT_HEIGHT 40 -#define TREE_SPRITE_WIDTH 40 /* text plus sprite entries only */ +typedef struct ro_treeview ro_treeview; -struct node_sprite { - osspriteop_area *area; - char name[12]; - char expanded_name[12]; +struct ro_treeview_table { + void (*open_menu)(wimp_pointer *pointer); }; -bool ro_gui_tree_initialise(void); -void ro_gui_tree_redraw(wimp_draw *redraw); -bool ro_gui_tree_click(wimp_pointer *pointer, struct tree *tree); -void ro_gui_tree_menu_closed(struct tree *tree); -bool ro_gui_tree_toolbar_click(wimp_pointer* pointer); -void ro_gui_tree_stop_edit(struct tree *tree); -void ro_gui_tree_open(wimp_open *open); -void ro_gui_tree_show(struct tree *tree); -bool ro_gui_tree_keypress(wimp_key *key); -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); -int ro_gui_tree_help(int x, int y); -void ro_gui_tree_update_theme(struct tree *tree); +ro_treeview *ro_treeview_create(wimp_w window, struct toolbar *toolbar, + unsigned int flags); +void ro_treeview_destroy(ro_treeview *tv); + +struct tree *ro_treeview_get_tree(ro_treeview *tv); +wimp_w ro_treeview_get_window(ro_treeview *tv); +bool ro_treeview_has_selection(ro_treeview *tv); + +void ro_treeview_set_origin(ro_treeview *tv, int x, int y); +void ro_treeview_mouse_at(wimp_w w, wimp_pointer *pointer); +bool ro_gui_treeview_toolbar_click(wimp_pointer *pointer); +void ro_treeview_update_theme(ro_treeview *tv); +int ro_treeview_get_help(help_full_message_request *message_data); #endif + diff --git a/riscos/url_complete.c b/riscos/url_complete.c index 580417a16..9649c5604 100644 --- a/riscos/url_complete.c +++ b/riscos/url_complete.c @@ -149,7 +149,7 @@ bool ro_gui_url_complete_keypress(struct gui_window *g, uint32_t key) if (url_complete_matches) { for (i = 0; i < MAXIMUM_VISIBLE_LINES; i++) { if (i < lines) { - url_complete_redraw[i] = + url_complete_redraw[i] = url_complete_matches[i]; } else { url_complete_redraw[i] = NULL; @@ -726,15 +726,13 @@ bool ro_gui_url_complete_click(wimp_pointer *pointer) ICON_TOOLBAR_URL, url_complete_matches[ url_complete_matches_selection], true); - global_history_add_recent(url_complete_matches[ - url_complete_matches_selection]); /** \todo The interaction of components here is hideous */ /* Do NOT make any attempt to use any of the global url * completion variables after this call to browser_window_go. * They will be invalidated by (at least): * + gui_window_set_url - * + destruction of (i)frames within the current page + * + destruction of (i)frames within the current page * Any attempt to use them will probably result in a crash. */ diff --git a/riscos/url_suggest.c b/riscos/url_suggest.c new file mode 100644 index 000000000..7a17c79a0 --- /dev/null +++ b/riscos/url_suggest.c @@ -0,0 +1,200 @@ +/* + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file + * URL Suggestion Menu (implementation). + */ + +#include <assert.h> +#include "oslib/wimp.h" +#include "content/content_type.h" +#include "content/urldb.h" +#include "riscos/menus.h" +#include "riscos/url_suggest.h" +#include "utils/messages.h" + +struct url_suggest_item { + const char *url; /*< The URL being stored. */ + unsigned int weight; /*< A weight assigned to the URL. */ + struct url_suggest_item *next; /*< The next URL in the list. */ +}; + +static bool url_suggest_callback(const char *url, const struct url_data *data); + +static wimp_menu *suggest_menu; +static int suggest_entries; +static time_t suggest_time; +static struct url_suggest_item *suggest_list; + +/** + * Initialise the URL suggestion menu. A menu block which must be set to + * contain URL_SUGGEST_MAX_URLS entries is passed in. + * + * /param *menu The menu to use as the suggestion menu. + * /return true if initialisation was OK; else false. + */ + +bool ro_gui_url_suggest_init(wimp_menu *menu) +{ + suggest_menu = menu; + + suggest_menu->title_data.indirected_text.text = + (char *) messages_get("URLSuggest"); + ro_gui_menu_init_structure((wimp_menu *) suggest_menu, + URL_SUGGEST_MAX_URLS); + + suggest_entries = 0; + + return true; +} + + +/** + * Builds the URL suggestion menu. This is called by ro_gui_menu_create() when + * it is asked to display the url_suggest_menu. + * + * /return true if the menu has entries; else false. + */ + +bool ro_gui_url_suggest_prepare_menu(void) +{ + int i; + struct url_suggest_item *list, *next; + + /* Fetch the URLs we want to include from URLdb. */ + + suggest_entries = 0; + suggest_list = NULL; + suggest_time = time(NULL); + + urldb_iterate_entries(url_suggest_callback); + + /* If any menu entries were found, put them into the menu. The list + * is in reverse order, last to first, so the menu is filled backwards. + * Entries from the list are freed as we go. + */ + + assert(suggest_entries <= URL_SUGGEST_MAX_URLS); + + if (suggest_entries > 0) { + i = suggest_entries; + + list = suggest_list; + suggest_list = NULL; + + while (list != NULL && i > 0) { + i--; + + suggest_menu->entries[i].menu_flags = 0; + suggest_menu->entries[i].data.indirected_text.text = + (char *) list->url; + suggest_menu->entries[i].data.indirected_text.size = + strlen(list->url) + 1; + + next = list->next; + free(list); + list = next; + } + + /* If i hasn't reached 0, then something went wrong -- get + * out now! + */ + + if (i > 0) + return false; + + suggest_menu->entries[0].menu_flags |= + wimp_MENU_TITLE_INDIRECTED; + suggest_menu->entries[suggest_entries - 1].menu_flags |= + wimp_MENU_LAST; + + return true; + } + + return false; +} + + +/** + * Callback function for urldb_iterate_entries + * + * \param url URL which matches + * \param data Data associated with URL + * \return true to continue iteration, false otherwise + */ + +bool url_suggest_callback(const char *url, const struct url_data *data) +{ + int count; + unsigned int weight; + struct url_suggest_item **list, *new, *old; + + /* Ignore unvisited URLs, and those that don't apply to HTML or Text. */ + + if (data->visits <= 0 ||(data->type != CONTENT_HTML && + data->type != CONTENT_TEXTPLAIN)) + return true; + + /* Calculate a weight for the URL. */ + + weight = (suggest_time - data->last_visit) / data->visits; + + /* Hunt through those URLs already found to see if we want to add + * this one. Smaller weights carry higher priority. + * + * The list is sorted into reverse order, so that lowest weight + * items are nearest the head. Therefore, items are dropped from + * the head, making things simpler. + */ + + list = &suggest_list; + count = 0; + + while (*list != NULL && weight < (*list)->weight) { + list = &((*list)->next); + count++; + } + + if (count > 0 || suggest_entries < URL_SUGGEST_MAX_URLS) { + new = (struct url_suggest_item *) + malloc(sizeof(struct url_suggest_item)); + + if (new != NULL) { + suggest_entries++; + new->url = url; + new->weight = weight; + new->next = *list; + + *list = new; + } + } + + /* If adding the URL gave us too many menu items, drop the lowest + * priority ones until the list is the right length again. + */ + + while (suggest_list != NULL && suggest_entries > URL_SUGGEST_MAX_URLS) { + old = suggest_list; + suggest_list = suggest_list->next; + free(old); + suggest_entries--; + } + + return true; +} + diff --git a/windows/history.c b/riscos/url_suggest.h index b0cf20495..d13b7d89a 100644 --- a/windows/history.c +++ b/riscos/url_suggest.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Vincent Sanders <vince@simtec.co.uk> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -16,26 +16,19 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "content/urldb.h" -#include "desktop/browser.h" - -void global_history_add(const char *url) -{ - const struct url_data *data; +/** \file + * URL Suggestion Menu (interface). + */ - data = urldb_get_url_data(url); - if (!data) - return; +#ifndef _NETSURF_RISCOS_URL_SUGGEST_H_ +#define _NETSURF_RISCOS_URL_SUGGEST_H_ +#include "oslib/wimp.h" -} +#define URL_SUGGEST_MAX_URLS 16 -void global_history_add_recent(const char *url) -{ -} +bool ro_gui_url_suggest_init(wimp_menu *menu); +bool ro_gui_url_suggest_prepare_menu(void); -char **global_history_get_recent(int *count) -{ - return NULL; -} +#endif diff --git a/riscos/wimp_event.c b/riscos/wimp_event.c index 4d14b1f0a..5d1f725f4 100644 --- a/riscos/wimp_event.c +++ b/riscos/wimp_event.c @@ -1,5 +1,6 @@ /* * Copyright 2005 Richard Wilson <info@tinct.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -95,6 +96,14 @@ struct event_window { void (*close_window)(wimp_w w); void (*redraw_window)(wimp_draw *redraw); void (*menu_selection)(wimp_w w, wimp_i i); + wimp_menu *window_menu; + bool window_menu_auto; + void (*window_menu_prepare)(wimp_w w, wimp_menu *m); + bool (*window_menu_selection)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action); + void (*window_menu_close)(wimp_w w, wimp_menu *m); + void (*window_menu_warning)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action); const char *help_prefix; void *user_data; struct icon_event *first; @@ -345,14 +354,27 @@ void *ro_gui_wimp_event_get_user_data(wimp_w w) /** * Handles a menu selection event. * + * (At present, this is tied to being called from menus.c and relies on that + * module decoding the menu into an action code. If menus.c loses its + * menu handling in the future, such decoding might need to move here.) + * + * The order of execution is: + * + * 1. Try to match the menu to a pop-up menu. If successful, handle it as + * this. + * 2. Try to match the menu to a window menu. If successful, pass control to + * the menu's registered _select handler. + * 3. Return event as unhandled. + * * \param w the window to owning the menu * \param i the icon owning the menu * \param menu the menu that has been selected * \param selection the selection information - * \return true if the event was handled, false otherwise + * \param action the menu action info from menus.c + * \return true if the event was handled, false otherwise */ bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu, - wimp_selection *selection) + wimp_selection *selection, menu_action action) { struct event_window *window; struct icon_event *event; @@ -367,11 +389,28 @@ bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu, if (!window) return false; + /* Start by looking for an icon event that matches. If there isn't one, + * then check to see if there is a window menu and associated + * selection handler available instead. + */ + for (event = window->first; event; event = event->next) if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i)) break; - if (!event) - return false; + if (!event) { + if ((window->window_menu) && (window->window_menu == menu) + && (window->window_menu_selection)) { + window->window_menu_selection(w, menu, + selection, action); + + /* Prepare the menu pending a possible Adjust click. */ + if (window->window_menu_prepare) + window->window_menu_prepare(w, menu); + return true; + } else { + return false; + } + } menu_entry = &menu->entries[selection->items[0]]; for (i = 1; selection->items[i] != -1; i++) @@ -433,10 +472,12 @@ bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu, * * The order of execution is: * - * 1. Any registered mouse_click routine (see ro_gui_wimp_register_mouse_click()) - * 2. If the current icon is not registered with a type then it is assumed that no + * 1. If a menu click, and the window has an automatic window menu, this is + * processed immediately. + * 2. Any registered mouse_click routine (see ro_gui_wimp_register_mouse_click()) + * 3. If the current icon is not registered with a type then it is assumed that no * action is necessary, and the click is deemed to have been handled. - * 3. If the registered mouse_click routine returned false, or there was no registered + * 4. If the registered mouse_click routine returned false, or there was no registered * routine then the automated action for the registered icon type is performed * * \param pointer the current pointer state @@ -458,7 +499,13 @@ bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer) if (!window) return false; - /* registered routines take priority */ + /* Menu clicks take priority if there is an auto menu defined. */ + if ((window->window_menu) && (window->window_menu_auto)) { + ro_gui_wimp_event_process_window_menu_click(pointer); + return true; + } + + /* registered routines take next priority */ if ((window->mouse_click) && (window->mouse_click(pointer))) return true; @@ -919,6 +966,32 @@ bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw) /** + * Process a Menu click in a window, by checking for a registered window + * menu and opening it if one is found. + * + * \param *p The pointer block from the mouse click event. + * \return true if the click was actioned; else false. + */ + +bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer) +{ + struct event_window *window; + + window = ro_gui_wimp_event_find_window(pointer->w); + if ((window) && (window->window_menu) + && (pointer->buttons == wimp_CLICK_MENU)) { + if (window->window_menu_prepare) + window->window_menu_prepare(window->w, + window->window_menu); + + ro_gui_menu_create(window->window_menu, + pointer->pos.x, pointer->pos.y, window->w); + return true; + } + return false; +} + +/** * Register a numeric field to be automatically handled */ bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i, @@ -1168,9 +1241,39 @@ bool ro_gui_wimp_event_register_menu_selection(wimp_w w, return true; } +/** + * Register a set of functions to be called to handle a window menu. + * + * \param + */ + +bool ro_gui_wimp_event_register_window_menu(wimp_w w, wimp_menu *m, + void (*callback_prepare)(wimp_w w, wimp_menu *m), + bool (*callback_selection)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action), + void (*callback_close)(wimp_w w, wimp_menu *m), + void (*callback_warning)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action), + bool menu_auto) +{ + struct event_window *window; + + window = ro_gui_wimp_event_get_window(w); + if (!window) + return false; + window->window_menu = m; + window->window_menu_prepare = callback_prepare; + window->window_menu_selection = callback_selection; + window->window_menu_close = callback_close; + window->window_menu_warning = callback_warning; + window->window_menu_auto = menu_auto; + return true; +} + /** - * Finds the event data associated with a given window handle, or creates a new one. + * Finds the event data associated with a given window handle, or creates a + * new one. * * \param w the window to find data for */ @@ -1273,12 +1376,88 @@ struct icon_event *ro_gui_wimp_event_get_event(wimp_w w, wimp_i i, return event; } +/* Handle sumbenu warnings. This is called from ro_gui_menu_warning(), and + * returns to that function to have the submenu opened correctly. + * + * \param w the window to owning the menu + * \param i the icon owning the menu + * \param menu the menu that has been selected + * \param selection the selection information + * \param action the menu action info from menus.c + * \return true if the event was handled, false otherwise + */ + +bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu, + wimp_selection *selection, menu_action action) +{ + struct event_window *window; + struct icon_event *event; + + ro_gui_wimp_event_register_submenu(0); + + /* Process the event for any window menus. Find the window data, then + * try and match to an icon event. If we can, then there isn't anything + * to do. + */ + + window = ro_gui_wimp_event_find_window(w); + if (!window) + return false; + + for (event = window->first; event; event = event->next) + if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i)) + break; + if (event) + return false; + + /* If the warning is for a window menu, then pass the event on to it. */ + + if ((window->window_menu) && (window->window_menu == menu)) { + if (window->window_menu_warning) { + window->window_menu_warning(w, menu, selection, action); + return true; + } + } + + return false; +}
+ /** - * Handle menus being closed + * Handle menus being closed. This is called from ro_gui_menu_closed(), in + * every scenario when one of our own menus is open. + * + * \param w the window to owning the menu + * \param i the icon owning the menu + * \param menu the menu that has been selected */ -void ro_gui_wimp_event_menus_closed(void) + +void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu) { - ro_gui_wimp_event_register_submenu(0); + struct event_window *window; + struct icon_event *event; + + ro_gui_wimp_event_register_submenu(0); + + /* Process the event for any window menus. Find the window data, then + * try and match to an icon event. If we can, then there isn't anything + * to do. + */ + + window = ro_gui_wimp_event_find_window(w); + if (!window) + return; + + for (event = window->first; event; event = event->next) + if ((event->type == EVENT_MENU_GRIGHT) && (event->i == i)) + break; + if (event) + return; + + /* If the close is for a window menu, then pass the event on to it. */ + + if ((window->window_menu) && (window->window_menu == menu) && + (window->window_menu_close)) + window->window_menu_close(w, menu); } /** diff --git a/riscos/wimp_event.h b/riscos/wimp_event.h index e8ae7842f..54ae95ae5 100644 --- a/riscos/wimp_event.h +++ b/riscos/wimp_event.h @@ -1,5 +1,6 @@ /*
* Copyright 2005 Richard Wilson <info@tinct.net>
+ * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> *
* This file is part of NetSurf, http://www.netsurf-browser.org/
*
@@ -30,7 +31,8 @@ #include <stdio.h>
#include <string.h>
#include "oslib/os.h"
-#include "oslib/wimp.h"
+#include "oslib/wimp.h" +#include "riscos/menus.h"
#define IS_WIMP_KEY (1u<<31)
@@ -45,12 +47,14 @@ bool ro_gui_wimp_event_set_user_data(wimp_w w, void *user); void *ro_gui_wimp_event_get_user_data(wimp_w w);
bool ro_gui_wimp_event_menu_selection(wimp_w w, wimp_i i, wimp_menu *menu,
- wimp_selection *selection);
+ wimp_selection *selection, menu_action action);
bool ro_gui_wimp_event_mouse_click(wimp_pointer *pointer);
bool ro_gui_wimp_event_keypress(wimp_key *key);
bool ro_gui_wimp_event_open_window(wimp_open *open);
bool ro_gui_wimp_event_close_window(wimp_w w);
-bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw);
+bool ro_gui_wimp_event_redraw_window(wimp_draw *redraw); + +bool ro_gui_wimp_event_process_window_menu_click(wimp_pointer *pointer);
bool ro_gui_wimp_event_register_numeric_field(wimp_w w, wimp_i i, wimp_i up,
wimp_i down, int min, int max, int stepping,
@@ -78,8 +82,18 @@ bool ro_gui_wimp_event_register_redraw_window(wimp_w w, void (*callback)(wimp_draw *redraw));
bool ro_gui_wimp_event_register_menu_selection(wimp_w w,
void (*callback)(wimp_w w, wimp_i i));
+bool ro_gui_wimp_event_register_window_menu(wimp_w w, wimp_menu *m, + void (*callback_prepare)(wimp_w w, wimp_menu *m), + bool (*callback_selection)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action), + void (*callback_close)(wimp_w w, wimp_menu *m), + void (*callback_warning)(wimp_w w, wimp_menu *m, + wimp_selection *s, menu_action action), + bool menu_auto); -void ro_gui_wimp_event_menus_closed(void);
+bool ro_gui_wimp_event_submenu_warning(wimp_w w, wimp_i i, wimp_menu *menu, + wimp_selection *selection, menu_action action);
+void ro_gui_wimp_event_menus_closed(wimp_w w, wimp_i i, wimp_menu *menu);
void ro_gui_wimp_event_register_submenu(wimp_w w);
#endif
diff --git a/riscos/window.c b/riscos/window.c index 198563059..55bdb11d6 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -5,6 +5,7 @@ * Copyright 2004 Andrew Timmins <atimmins@blueyonder.co.uk> * Copyright 2005 Richard Wilson <info@tinct.net> * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net> + * Copyright 2010 Stephen Fryatt <stevef@netsurf-browser.org> * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -150,7 +151,6 @@ static bool ro_gui_window_keypress(wimp_key *key); static void ro_gui_window_launch_url(struct gui_window *g, const char *url); static void ro_gui_window_clone_options(struct browser_window *new_bw, struct browser_window *old_bw); -static browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons); static bool ro_gui_window_import_text(struct gui_window *g, const char *filename, bool toolbar); @@ -398,7 +398,7 @@ struct gui_window *gui_create_browser_window(struct browser_window *bw, state.next = wimp_TOP; if (bw->parent) { top = browser_window_owner(bw); - error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), + error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), top->window->window, wimp_CHILD_LINKS_PARENT_WORK_AREA << wimp_CHILD_XORIGIN_SHIFT | @@ -1110,7 +1110,7 @@ void gui_window_stop_throbber(struct gui_window *g) * set favicon */ void gui_window_set_icon(struct gui_window *g, hlcache_handle *icon) -{ +{ } /** @@ -1688,7 +1688,6 @@ void ro_gui_window_launch_url(struct gui_window *g, const char *url) if (res == URL_FUNC_OK) { gui_window_set_url(g, url_norm); browser_window_go(g->bw, url_norm, 0, true); - global_history_add_recent(url_norm); free(url_norm); } } @@ -2124,7 +2123,8 @@ void ro_gui_window_mouse_at(struct gui_window *g, wimp_pointer *pointer) if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos)) browser_window_mouse_track(g->bw, - ro_gui_mouse_drag_state(pointer->buttons), + ro_gui_mouse_drag_state(pointer->buttons, + wimp_BUTTON_CLICK_DRAG), pos.x, pos.y); } @@ -2321,7 +2321,8 @@ bool ro_gui_window_click(wimp_pointer *pointer) } else { if (ro_gui_window_to_window_pos(g, pointer->pos.x, pointer->pos.y, &pos)) browser_window_mouse_click(g->bw, - ro_gui_mouse_click_state(pointer->buttons), + ro_gui_mouse_click_state(pointer->buttons, + wimp_BUTTON_CLICK_DRAG), pos.x, pos.y); } return true; @@ -3088,17 +3089,35 @@ void ro_gui_window_prepare_navigate_all(void) { /** * Returns the state of the mouse buttons and modifiers keys for a * click/release action, suitable for passing to the OS-independent - * browser window code + * browser window/ treeview/ etc code. + * + * \param buttons Wimp button state. + * \param type Wimp work-area/icon type for decoding. + * \return NetSurf core button state. */ -browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons) +browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons, + wimp_icon_flags type) { browser_mouse_state state = 0; - if (buttons & (wimp_CLICK_SELECT)) - state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1; - if (buttons & (wimp_CLICK_ADJUST)) - state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2; + switch (type) { + case wimp_BUTTON_CLICK_DRAG: + if (buttons & (wimp_CLICK_SELECT)) + state |= BROWSER_MOUSE_PRESS_1 | BROWSER_MOUSE_CLICK_1; + if (buttons & (wimp_CLICK_ADJUST)) + state |= BROWSER_MOUSE_PRESS_2 | BROWSER_MOUSE_CLICK_2; + break; + case wimp_BUTTON_DOUBLE_CLICK_DRAG: + if (buttons & (wimp_SINGLE_SELECT)) + state |= BROWSER_MOUSE_CLICK_1; + if (buttons & (wimp_SINGLE_ADJUST)) + state |= BROWSER_MOUSE_CLICK_2; + if (buttons & (wimp_DOUBLE_SELECT)) + state |= BROWSER_MOUSE_CLICK_1 | + BROWSER_MOUSE_DOUBLE_CLICK; + break; + } if (buttons & (wimp_DRAG_SELECT)) { state |= BROWSER_MOUSE_DRAG_1; @@ -3111,30 +3130,46 @@ browser_mouse_state ro_gui_mouse_click_state(wimp_mouse_state buttons) if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1; if (ro_gui_ctrl_pressed()) state |= BROWSER_MOUSE_MOD_2; + if (ro_gui_alt_pressed()) state |= BROWSER_MOUSE_MOD_3; return state; } - /** * Returns the state of the mouse buttons and modifiers keys whilst - * dragging, for passing to the OS-independent browser window code + * dragging, for passing to the OS-independent browser window/ treeview/ + * etc code + * + * \param buttons Wimp button state. + * \param type Wimp work-area/icon type for decoding. + * \return NetSurf core button state. */ -browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons) +browser_mouse_state ro_gui_mouse_drag_state(wimp_mouse_state buttons, + wimp_icon_flags type) { browser_mouse_state state = 0; + switch (type) { + case wimp_BUTTON_CLICK_DRAG: + case wimp_BUTTON_DOUBLE_CLICK_DRAG: + if (buttons & (wimp_CLICK_SELECT)) + state |= BROWSER_MOUSE_HOLDING_1; + if (buttons & (wimp_CLICK_ADJUST)) + state |= BROWSER_MOUSE_HOLDING_2; - if (buttons & (wimp_CLICK_SELECT)) state |= BROWSER_MOUSE_HOLDING_1; - if (buttons & (wimp_CLICK_ADJUST)) state |= BROWSER_MOUSE_HOLDING_2; + if (!(buttons & (wimp_CLICK_SELECT) || + buttons & (wimp_CLICK_ADJUST))) + mouse_drag = false; + break; + } - if (!(buttons & (wimp_CLICK_SELECT) || buttons & (wimp_CLICK_ADJUST))) - mouse_drag = false; - if (mouse_drag) state |= BROWSER_MOUSE_DRAG_ON; + if (mouse_drag) + state |= BROWSER_MOUSE_DRAG_ON; if (ro_gui_shift_pressed()) state |= BROWSER_MOUSE_MOD_1; if (ro_gui_ctrl_pressed()) state |= BROWSER_MOUSE_MOD_2; + if (ro_gui_alt_pressed()) state |= BROWSER_MOUSE_MOD_3; return state; } @@ -3216,7 +3251,8 @@ void ro_gui_window_scroll_end(struct gui_window *g, wimp_dragged *drag) if (ro_gui_window_to_window_pos(g, drag->final.x0, drag->final.y0, &pos)) browser_window_mouse_drag_end(g->bw, - ro_gui_mouse_click_state(pointer.buttons), + ro_gui_mouse_click_state(pointer.buttons, + wimp_BUTTON_CLICK_DRAG), pos.x, pos.y); } diff --git a/utils/messages.c b/utils/messages.c index 2f3bc173e..6ad17c3dd 100644 --- a/utils/messages.c +++ b/utils/messages.c @@ -30,6 +30,8 @@ #include <stdbool.h> #include <string.h> #include <zlib.h> +#include <stdarg.h> + #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" @@ -158,6 +160,35 @@ const char *messages_get_ctx(const char *key, struct hash_table *ctx) return r ? r : key; } +/* exported interface documented in messages.h */ +char *messages_get_buff(const char *key, ...) +{ + const char *msg_fmt; + char *buff = NULL; /* formatted buffer to return */ + int buff_len = 0; + va_list ap; + + msg_fmt = messages_get_ctx(key, messages_hash); + + va_start(ap, key); + buff_len = vsnprintf(buff, buff_len, msg_fmt, ap); + va_end(ap); + + buff = malloc(buff_len + 1); + + if (buff == NULL) { + LOG(("malloc failed")); + warn_user("NoMemory", 0); + } else { + va_start(ap, key); + vsnprintf(buff, buff_len + 1, msg_fmt, ap); + va_end(ap); + } + + return buff; +} + + /** * Fast lookup of a message by key from the standard Messages hash. * diff --git a/utils/messages.h b/utils/messages.h index 1c72db2f8..2b78d07ee 100644 --- a/utils/messages.h +++ b/utils/messages.h @@ -40,4 +40,16 @@ struct hash_table *messages_load_ctx(const char *path, struct hash_table *ctx); const char *messages_get_ctx(const char *key, struct hash_table *ctx); const char *messages_get(const char *key); +/** + * Formatted message from a key in the global message hash. + * + * \param key key of message + * \param ... message parameters + * \return buffer containing formatted message text or NULL if memory + * is unavailable. The caller owns the returned buffer and is + * responsible for freeing it. + */ + +char *messages_get_buff(const char *key, ...); + #endif diff --git a/windows/tree.c b/windows/tree.c index e5cb390b5..61e32e0a5 100644 --- a/windows/tree.c +++ b/windows/tree.c @@ -18,45 +18,56 @@ #include "desktop/tree.h" -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(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) -{ -} +const char tree_directory_icon_name[] = "directory.png"; +const char tree_content_icon_name[] = "content.png"; -void tree_recalculate_node_element(struct node_element *element) -{ -} -void tree_update_URL_node(struct node *node, const char *url, - const struct url_data *data) -{ -} -void tree_resized(struct tree *tree) -{ -} -void tree_set_node_sprite_folder(struct node *node) -{ -} - -void tree_set_node_sprite(struct node *node, const char *sprite, - const char *expanded) +/** + * Translates a content_type to the name of a respective icon + * + * \param content_type content type + * \param buffer buffer for the icon name + */ +void tree_icon_name_from_content_type(char *buffer, content_type type) { + // TODO: design/acquire icons + switch (type) { + case CONTENT_HTML: + case CONTENT_TEXTPLAIN: + case CONTENT_CSS: +#if defined(WITH_MNG) || defined(WITH_PNG) + case CONTENT_PNG: +#endif +#ifdef WITH_MNG + case CONTENT_JNG: + case CONTENT_MNG: +#endif +#ifdef WITH_JPEG + case CONTENT_JPEG: +#endif +#ifdef WITH_GIF + case CONTENT_GIF: +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + case CONTENT_ICO: +#endif +#ifdef WITH_SPRITE + case CONTENT_SPRITE: +#endif +#ifdef WITH_DRAW + case CONTENT_DRAW: +#endif +#ifdef WITH_ARTWORKS + case CONTENT_ARTWORKS: +#endif +#ifdef WITH_NS_SVG + case CONTENT_SVG: +#endif + default: + sprintf(buffer, tree_content_icon_name); + break; + } } - |