NetSurf
hotlist.c
Go to the documentation of this file.
1/*
2 * Copyright 2012 John-Mark Bell <jmb@netsurf-browser.org>
3 * Copyright 2013 Michael Drake <tlsa@netsurf-browser.org>
4 *
5 * This file is part of NetSurf, http://www.netsurf-browser.org/
6 *
7 * NetSurf is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * NetSurf is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <errno.h>
21#include <assert.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include <dom/dom.h>
26#include <dom/bindings/hubbub/parser.h>
27
28#include "utils/corestrings.h"
29#include "utils/messages.h"
30#include "utils/utils.h"
31#include "utils/utf8.h"
32#include "utils/libdom.h"
33#include "utils/log.h"
34#include "utils/nsurl.h"
35#include "content/urldb.h"
36
37#include "netsurf/misc.h"
39#include "desktop/hotlist.h"
40#include "desktop/treeview.h"
42
43#define N_DAYS 28
44#define N_SEC_PER_DAY (60 * 60 * 24)
45
53};
54
58};
59
63 bool built;
65 char *save_path;
67};
69
73
75};
76
77
78/*
79 * Get path for writing hotlist to
80 *
81 * \param path The final path of the hotlist
82 * \param loaded Updated to the path to write the holist to
83 * \return NSERROR_OK on success, or appropriate error otherwise
84 */
85static nserror hotlist_get_temp_path(const char *path, char **temp_path)
86{
87 const char *extension = "-bk";
88 char *joined;
89 int len;
90
91 len = strlen(path) + strlen(extension);
92
93 joined = malloc(len + 1);
94 if (joined == NULL) {
95 return NSERROR_NOMEM;
96 }
97
98 if (snprintf(joined, len + 1, "%s%s", path, extension) != len) {
99 free(joined);
100 return NSERROR_UNKNOWN;
101 }
102
103 *temp_path = joined;
104 return NSERROR_OK;
105}
106
107
108/* Save the hotlist to to a file at the given path
109 *
110 * \param path Path to save hotlist file to. NULL path is a no-op.
111 * \return NSERROR_OK on success, or appropriate error otherwise
112 */
113static nserror hotlist_save(const char *path)
114{
115 nserror res = NSERROR_OK;
116 char *temp_path;
117
118 /* NULL path is a no-op. */
119 if (path == NULL) {
120 return NSERROR_OK;
121 }
122
123 /* Get path to export to */
124 res = hotlist_get_temp_path(path, &temp_path);
125 if (res != NSERROR_OK) {
126 return res;
127 }
128
129 /* Export to temp path */
130 res = hotlist_export(temp_path, NULL);
131 if (res != NSERROR_OK) {
132 goto cleanup;
133 }
134
135 /* Remove old hotlist to handle non-POSIX rename() implementations. */
136 (void)remove(path);
137
138 /* Replace any old hotlist file with the one we just saved */
139 if (rename(temp_path, path) != 0) {
141 NSLOG(netsurf, INFO, "Error renaming hotlist: %s.",
142 strerror(errno));
143 goto cleanup;
144 }
145
146cleanup:
147 free(temp_path);
148
149 return res;
150}
151
152
153/**
154 * Scheduler callback for saving the hotlist.
155 *
156 * \param p Unused user data.
157 */
158static void hotlist_schedule_save_cb(void *p)
159{
160 hl_ctx.save_scheduled = false;
162}
163
164
165/**
166 * Schedule a hotlist save.
167 *
168 * \return NSERROR_OK on success, or appropriate error otherwise
169 */
171{
172 if (hl_ctx.save_scheduled == false && hl_ctx.save_path != NULL) {
173 nserror err = guit->misc->schedule(10 * 1000,
175 if (err != NSERROR_OK) {
176 return err;
177 }
178 hl_ctx.save_scheduled = true;
179 }
180
181 return NSERROR_OK;
182}
183
184
185/**
186 * Set a hotlist entry's data from the url_data.
187 *
188 * \param e hotlist entry to set up.
189 * \param data Data associated with entry's URL.
190 * \return NSERROR_OK on success, appropriate error otherwise.
191 */
193 struct hotlist_entry *e, const struct url_data *data)
194{
195 char buffer[16];
196 char *last_visited = NULL;
197 size_t len = 0;
198
199 /* Last visited */
200 if (data->visits != 0) {
201 const size_t lvsize = 256;
202 struct tm *lvtime;
203
204 if ((lvtime = localtime(&data->last_visit)) != NULL) {
205 last_visited = malloc(lvsize);
206 if (last_visited != NULL) {
207 len = strftime(last_visited, lvsize,
208 "%a %b %e %H:%M:%S %Y",
209 lvtime);
210 }
211 }
212 } else {
213 last_visited = strdup("-");
214 len = 1;
215 }
216 if (last_visited == NULL) {
217 return NSERROR_NOMEM;
218 }
219
221 e->data[HL_LAST_VISIT].value = last_visited;
222 e->data[HL_LAST_VISIT].value_len = len;
223
224 /* Visits */
225 len = snprintf(buffer, 16, "%u", data->visits);
226 if (len == 16) {
227 len--;
228 buffer[len] = '\0';
229 }
230
232 e->data[HL_VISITS].value = strdup(buffer);
233 if (e->data[HL_VISITS].value == NULL) {
234 free((void*)e->data[HL_LAST_VISIT].value);
235 return NSERROR_NOMEM;
236 }
237 e->data[HL_VISITS].value_len = len;
238
239 return NSERROR_OK;
240}
241
242
243/**
244 * Set a hotlist entry's data from the url_data.
245 *
246 * \param e hotlist entry to set up
247 * \param title Title for entry, or NULL if using title from data
248 * \param data Data associated with entry's URL
249 * \return NSERROR_OK on success, appropriate error otherwise
250 */
252 struct hotlist_entry *e, const char *title,
253 const struct url_data *data)
254{
255 nserror err;
256
257 /* "URL" field */
259 e->data[HL_URL].value = nsurl_access(e->url);
261
262 /* "Title" field */
263 if (title == NULL) {
264 /* Title not provided; use one from URL data */
265 title = (data->title != NULL) ?
266 data->title : nsurl_access(e->url);
267 }
268
270 e->data[HL_TITLE].value = strdup(title);
271 if (e->data[HL_TITLE].value == NULL) {
272 return NSERROR_NOMEM;
273 }
274 e->data[HL_TITLE].value_len = (e->data[HL_TITLE].value != NULL) ?
275 strlen(title) : 0;
276
277 /* "Last visited" and "Visits" fields */
279 if (err != NSERROR_OK) {
280 free((void*)e->data[HL_TITLE].value);
281 return NSERROR_OK;
282 }
283
284 return NSERROR_OK;
285}
286
287
288/**
289 * Add a hotlist entry to the treeview
290 *
291 * \param e Entry to add to treeview
292 * \param relation Existing node to insert as relation of, or NULL
293 * \param rel Folder's relationship to relation
294 * \return NSERROR_OK on success, or appropriate error otherwise
295 *
296 * It is assumed that the entry is unique (for its URL) in the global
297 * hotlist table
298 */
300 treeview_node *relation, enum treeview_relationship rel)
301{
302 nserror err;
303
305 relation, rel, e->data, e, hl_ctx.built ?
308 if (err != NSERROR_OK) {
309 return err;
310 }
311
312 return NSERROR_OK;
313}
314
315
316/**
317 * Delete a hotlist entry
318 *
319 * This does not delete the treeview node, rather it should only be called from
320 * the treeview node delete event message.
321 *
322 * \param e Entry to delete
323 */
325{
326 assert(e != NULL);
327 assert(e->entry == NULL);
328
329 /* Destroy fields */
330 free((void *)e->data[HL_TITLE].value); /* Eww */
331 free((void *)e->data[HL_LAST_VISIT].value); /* Eww */
332 free((void *)e->data[HL_VISITS].value); /* Eww */
333 nsurl_unref(e->url);
334
335 /* Destroy entry */
336 free(e);
337}
338
339
340/**
341 * Create hotlist entry data for URL.
342 *
343 * \param url URL for entry to add to hotlist.
344 * \param title Title for entry, or NULL if using title from data
345 * \param data URL data for the entry, or NULL
346 * \param entry Updated to new hotlist entry data
347 * \return NSERROR_OK on success, or appropriate error otherwise
348 */
349static nserror hotlist_create_entry(nsurl *url, const char *title,
350 const struct url_data *data, struct hotlist_entry **entry)
351{
352 nserror err;
353 struct hotlist_entry *e;
354
355 assert(url != NULL);
356
357 *entry = NULL;
358
359 if (data == NULL) {
360 /* Get the URL data */
362 if (data == NULL) {
363 /* No entry in database, so add one */
365 /* now attempt to get url data */
367 }
368 if (data == NULL) {
369 return NSERROR_NOMEM;
370 }
371 }
372
373 /* Create new local hotlist entry */
374 e = malloc(sizeof(struct hotlist_entry));
375 if (e == NULL) {
376 return NSERROR_NOMEM;
377 }
378
379 e->url = nsurl_ref(url);
380 e->entry = NULL;
381
383 if (err != NSERROR_OK) {
384 nsurl_unref(e->url);
385 free(e);
386 return err;
387 }
388
389 *entry = e;
390
391 return NSERROR_OK;
392}
393
394
395/**
396 * Add an entry to the hotlist (creates the entry).
397 *
398 * \param url URL for entry to add to hotlist.
399 * \param title Title for entry, or NULL if using title from data
400 * \param data URL data for the entry, or NULL
401 * \param relation Existing node to insert as relation of, or NULL
402 * \param rel Entry's relationship to relation
403 * \param entry Updated to new treeview entry node
404 * \return NSERROR_OK on success, or appropriate error otherwise
405 */
406static nserror hotlist_add_entry_internal(nsurl *url, const char *title,
407 const struct url_data *data, treeview_node *relation,
409{
410 nserror err;
411 struct hotlist_entry *e;
412
413 err = hotlist_create_entry(url, title, data, &e);
414 if (err != NSERROR_OK) {
415 return err;
416 }
417
418 err = hotlist_entry_insert(e, relation, rel);
419 if (err != NSERROR_OK) {
421 return err;
422 }
423
424 /* Make this URL persistent */
426
427 *entry = e->entry;
428
429 return NSERROR_OK;
430}
431
432
433/**
434 * Add folder to the hotlist (creates the folder).
435 *
436 * \param title Title for folder, or NULL if using title from data
437 * \param relation Existing node to insert as relation of, or NULL
438 * \param rel Folder's relationship to relation
439 * \param folder Updated to new hotlist folder data
440 * \param default_folder Add to the default folder.
441 * \return NSERROR_OK on success, or appropriate error otherwise
442 */
444 const char *title, treeview_node *relation,
445 enum treeview_relationship rel, struct hotlist_folder **folder,
446 bool default_folder)
447{
448 struct hotlist_folder *f;
450 treeview_node *n;
451 nserror err;
452
453 if (title == NULL) {
454 title = messages_get("NewFolderName");
455 }
456
457 /* Create the title field */
458 f = malloc(sizeof(struct hotlist_folder));
459 if (f == NULL) {
460 return NSERROR_NOMEM;
461 }
463 f->data.value = strdup(title);
464 if (f->data.value == NULL) {
465 free(f);
466 return NSERROR_NOMEM;
467 }
468 f->data.value_len = strlen(title);
469
470 if (!hl_ctx.built)
473 if (default_folder)
475
477 &n, relation, rel, &f->data, f, flags);
478 if (err != NSERROR_OK) {
479 free((void*)f->data.value); /* Eww */
480 free(f);
481 return err;
482 }
483
484 f->folder = n;
485 *folder = f;
486
487 return NSERROR_OK;
488}
489
490
492 struct treeview_node_msg msg, void *data)
493{
494 struct hotlist_folder *f = data;
495 const char *old_text;
496 bool match;
497
498 switch (msg.msg) {
500 if (f == hl_ctx.default_folder)
501 hl_ctx.default_folder = NULL;
502 free((void*)f->data.value); /* Eww */
503 free(f);
504 break;
505
507 if (lwc_string_isequal(hl_ctx.fields[HL_FOLDER].field,
508 msg.data.node_edit.field, &match) ==
509 lwc_error_ok && match == true &&
510 msg.data.node_edit.text != NULL &&
511 msg.data.node_edit.text[0] != '\0') {
512 /* Requst to change the folder title text */
513 old_text = f->data.value;
514 f->data.value = strdup(msg.data.node_edit.text);
515
516 if (f->data.value == NULL) {
517 f->data.value = old_text;
518 } else {
519 f->data.value_len = strlen(f->data.value);
521 f->folder, &f->data, f);
522 free((void *)old_text);
523 }
524 }
525 break;
526
528 break;
529 }
530
531 return NSERROR_OK;
532}
533
534/**
535 * callback for hotlist treeview entry manipulation.
536 */
537static nserror
539{
540 struct hotlist_entry *e = data;
541 const char *old_text;
542 nsurl *old_url;
543 nsurl *url;
544 nserror err = NSERROR_OK;
545 bool match;
546
547 switch (msg.msg) {
549 e->entry = NULL;
551
552 err = hotlist_schedule_save();
553 break;
554
556 if (lwc_string_isequal(hl_ctx.fields[HL_TITLE].field,
557 msg.data.node_edit.field, &match) ==
558 lwc_error_ok && match == true &&
559 msg.data.node_edit.text != NULL &&
560 msg.data.node_edit.text[0] != '\0') {
561 /* Requst to change the entry title text */
562 old_text = e->data[HL_TITLE].value;
563 e->data[HL_TITLE].value =
564 strdup(msg.data.node_edit.text);
565
566 if (e->data[HL_TITLE].value == NULL) {
567 e->data[HL_TITLE].value = old_text;
568 } else {
570 strlen(e->data[HL_TITLE].value);
572 e->entry, e->data, e);
573 free((void *)old_text);
574 }
575
576 err = hotlist_schedule_save();
577
578 } else if (lwc_string_isequal(hl_ctx.fields[HL_URL].field,
579 msg.data.node_edit.field, &match) ==
580 lwc_error_ok && match == true &&
581 msg.data.node_edit.text != NULL &&
582 msg.data.node_edit.text[0] != '\0') {
583 /* Requst to change the entry URL text */
584 err = nsurl_create(msg.data.node_edit.text, &url);
585 if (err == NSERROR_OK) {
586 old_url = e->url;
587
588 e->url = url;
591
593 e->entry, e->data, e);
594 nsurl_unref(old_url);
595
596 err = hotlist_schedule_save();
597 }
598 }
599 break;
600
602 {
603 struct browser_window *existing = NULL;
605
606 /* TODO: Set existing to window that new tab appears in */
607
608 if (msg.data.node_launch.mouse &
610 existing == NULL) {
611 /* Shift or Ctrl launch, open in new window rather
612 * than tab. */
613 /* TODO: flags ^= BW_CREATE_TAB; */
614 }
615
616 err = browser_window_create(flags, e->url, NULL,
617 existing, NULL);
618 }
619 break;
620 }
621 return err;
622}
623
627};
628
629
630
631typedef struct {
636 dom_string *title;
638
639
640/**
641 * Parse an entry represented as a li.
642 *
643 * \param li DOM node for parsed li
644 * \param ctx Our hotlist loading context.
645 * \return NSERROR_OK on success, or appropriate error otherwise
646 */
648{
649 dom_node *a;
650 dom_string *title1, *url1;
651 const char *title;
652 nsurl *url;
653 dom_exception derror;
654 nserror err;
655
656 /* The li must contain an "a" element */
657 a = libdom_find_first_element(li, corestring_lwc_a);
658 if (a == NULL) {
659 NSLOG(netsurf, INFO, "Missing <a> in <li>");
660 return NSERROR_INVALID;
661 }
662
663 derror = dom_node_get_text_content(a, &title1);
664 if (derror != DOM_NO_ERR) {
665 NSLOG(netsurf, INFO, "No title");
666 dom_node_unref(a);
667 return NSERROR_INVALID;
668 }
669
670 derror = dom_element_get_attribute(a, corestring_dom_href, &url1);
671 if (derror != DOM_NO_ERR || url1 == NULL) {
672 NSLOG(netsurf, INFO, "No URL");
673 dom_string_unref(title1);
674 dom_node_unref(a);
675 return NSERROR_INVALID;
676 }
677 dom_node_unref(a);
678
679 if (title1 != NULL) {
680 title = dom_string_data(title1);
681 } else {
682 title = messages_get("NoTitle");
683 }
684
685 /* Need to get URL as a nsurl object */
686 err = nsurl_create(dom_string_data(url1), &url);
687 dom_string_unref(url1);
688
689 if (err != NSERROR_OK) {
690 NSLOG(netsurf, INFO, "Failed normalising '%s'",
691 dom_string_data(url1));
692
693 if (title1 != NULL) {
694 dom_string_unref(title1);
695 }
696
697 return err;
698 }
699
700 /* Add the entry */
701 err = hotlist_add_entry_internal(url, title, NULL, ctx->rel,
702 ctx->relshp, &ctx->rel);
703 nsurl_unref(url);
704 if (title1 != NULL) {
705 dom_string_unref(title1);
706 }
708
709 if (err != NSERROR_OK) {
710 return err;
711 }
712
713 return NSERROR_OK;
714}
715
716
717/*
718 * Callback for libdom_iterate_child_elements, which despite the namespace is
719 * a NetSurf function.
720 *
721 * \param node Node that is a child of the directory UL node
722 * \param ctx Our hotlist loading context.
723 */
724static nserror hotlist_load_directory_cb(dom_node *node, void *ctx);
725
726/**
727 * Parse a directory represented as a ul.
728 *
729 * \param ul DOM node for parsed ul.
730 * \param ctx The hotlist context.
731 * \return NSERROR_OK on success, or appropriate error otherwise
732 */
734{
735 assert(ul != NULL);
736 assert(ctx != NULL);
737
740}
741
742
743/* Documented above, in forward declaration */
744nserror hotlist_load_directory_cb(dom_node *node, void *ctx)
745{
746 /* TODO: return appropriate errors */
747 hotlist_load_ctx *current_ctx = ctx;
748 dom_string *name;
749 dom_exception error;
750 nserror err;
751
752 /* The ul may contain entries as a li, or directories as
753 * an h4 followed by a ul. Non-element nodes may be present
754 * (eg. text, comments), and are ignored. */
755
756 error = dom_node_get_node_name(node, &name);
757 if (error != DOM_NO_ERR || name == NULL)
758 return NSERROR_DOM;
759
760 if (dom_string_caseless_lwc_isequal(name, corestring_lwc_li)) {
761 /* Entry handling */
762 hotlist_load_entry(node, current_ctx);
763 current_ctx->last_was_h4 = false;
764
765 } else if (dom_string_caseless_lwc_isequal(name, corestring_lwc_h4)) {
766 /* Directory handling, part 1: Get title from H4 */
767 dom_string *title;
768
769 error = dom_node_get_text_content(node, &title);
770 if (error != DOM_NO_ERR || title == NULL) {
771 NSLOG(netsurf, INFO,
772 "Empty <h4> or memory exhausted.");
773 dom_string_unref(name);
774 return NSERROR_DOM;
775 }
776
777 if (current_ctx->title != NULL)
778 dom_string_unref(current_ctx->title);
779 current_ctx->title = title;
780 current_ctx->last_was_h4 = true;
781
782 } else if (current_ctx->last_was_h4 &&
783 dom_string_caseless_lwc_isequal(name,
784 corestring_lwc_ul)) {
785 /* Directory handling, part 2: Make node, and handle children */
786 const char *title;
787 dom_string *id;
788 struct hotlist_folder *f;
789 hotlist_load_ctx new_ctx;
790 treeview_node *rel;
791 bool default_folder = false;
792
793 /* Check if folder should be default folder */
794 error = dom_element_get_attribute(node, corestring_dom_id, &id);
795 if (error != DOM_NO_ERR) {
796 dom_string_unref(name);
797 return NSERROR_NOMEM;
798 }
799 if (id != NULL) {
800 if (dom_string_lwc_isequal(id, corestring_lwc_default))
801 default_folder = true;
802
803 dom_string_unref(id);
804 }
805
806 title = dom_string_data(current_ctx->title);
807
808 /* Add folder node */
809 err = hotlist_add_folder_internal(title, current_ctx->rel,
810 current_ctx->relshp, &f, default_folder);
811 if (err != NSERROR_OK) {
812 dom_string_unref(name);
813 return NSERROR_NOMEM;
814 }
815
816 if (default_folder)
818
819 rel = f->folder;
820 current_ctx->rel = rel;
821 current_ctx->relshp = TREE_REL_NEXT_SIBLING;
822
823 new_ctx.tree = current_ctx->tree;
824 new_ctx.rel = rel;
826 new_ctx.last_was_h4 = false;
827 new_ctx.title = NULL;
828
829 /* And load its contents */
830 err = hotlist_load_directory(node, &new_ctx);
831 if (err != NSERROR_OK) {
832 dom_string_unref(name);
833 return NSERROR_NOMEM;
834 }
835
836 if (new_ctx.title != NULL) {
837 dom_string_unref(new_ctx.title);
838 new_ctx.title = NULL;
839 }
840 current_ctx->last_was_h4 = false;
841 } else {
842 current_ctx->last_was_h4 = false;
843 }
844
845 dom_string_unref(name);
846
847 return NSERROR_OK;
848}
849
850
851/*
852 * Load the hotlist data from file
853 *
854 * \param path The path to load the hotlist file from, or NULL
855 * \param loaded Updated to true iff hotlist file loaded, else set false
856 * \return NSERROR_OK on success, or appropriate error otherwise
857 */
858static nserror hotlist_load(const char *path, bool *loaded)
859{
860 dom_document *document;
861 dom_node *html, *body, *ul;
863 char *temp_path;
864 nserror err;
865
866 *loaded = false;
867
868 /* Handle no path */
869 if (path == NULL) {
870 NSLOG(netsurf, INFO, "No hotlist file path provided.");
871 return NSERROR_OK;
872 }
873
874 /* Get temp hotlist write path */
875 err = hotlist_get_temp_path(path, &temp_path);
876 if (err != NSERROR_OK) {
877 return err;
878 }
879
880 /* Load hotlist file */
881 err = libdom_parse_file(path, "iso-8859-1", &document);
882 if (err != NSERROR_OK) {
883 err = libdom_parse_file(temp_path, "iso-8859-1", &document);
884 if (err != NSERROR_OK) {
885 free(temp_path);
886 return err;
887 }
888 }
889 free(temp_path);
890
891 /* Find HTML element */
892 html = libdom_find_first_element((dom_node *) document,
893 corestring_lwc_html);
894 if (html == NULL) {
895 dom_node_unref(document);
896 NSLOG(netsurf, WARNING,
897 "%s (<html> not found)",
898 messages_get("TreeLoadError"));
899 return NSERROR_OK;
900 }
901
902 /* Find BODY element */
903 body = libdom_find_first_element(html, corestring_lwc_body);
904 if (body == NULL) {
905 dom_node_unref(html);
906 dom_node_unref(document);
907 NSLOG(netsurf, WARNING,
908 "%s (<html>...<body> not found)",
909 messages_get("TreeLoadError"));
910 return NSERROR_OK;
911 }
912
913 /* Find UL element */
914 ul = libdom_find_first_element(body, corestring_lwc_ul);
915 if (ul == NULL) {
916 dom_node_unref(body);
917 dom_node_unref(html);
918 dom_node_unref(document);
919 NSLOG(netsurf, WARNING,
920 "%s (<html>...<body>...<ul> not found.)",
921 messages_get("TreeLoadError"));
922 return NSERROR_OK;
923 }
924
925 /* Set up the hotlist loading context */
926 ctx.tree = hl_ctx.tree;
927 ctx.rel = NULL;
929 ctx.last_was_h4 = false;
930 ctx.title = NULL;
931
932 err = hotlist_load_directory(ul, &ctx);
933
934 if (ctx.title != NULL) {
935 dom_string_unref(ctx.title);
936 ctx.title = NULL;
937 }
938
939 dom_node_unref(ul);
940 dom_node_unref(body);
941 dom_node_unref(html);
942 dom_node_unref(document);
943
944 if (err != NSERROR_OK) {
945 NSLOG(netsurf, WARNING,
946 "%s (Failed building tree.)",
947 messages_get("TreeLoadError"));
948 return NSERROR_OK;
949 }
950
951 *loaded = true;
952
953 return NSERROR_OK;
954}
955
956
957/*
958 * Generate default hotlist
959 *
960 * \return NSERROR_OK on success, or appropriate error otherwise
961 */
963{
964 int i;
965 struct hotlist_folder *f;
966 treeview_node *e;
967 const char *title;
968 nserror err;
969 nsurl *url;
970 static const struct {
971 const char *url;
972 const char *msg_key;
973 } default_entries[] = {
974 { "https://www.netsurf-browser.org/",
975 "HotlistHomepage" },
976 { "https://www.netsurf-browser.org/downloads/",
977 "HotlistDownloads" },
978 { "https://www.netsurf-browser.org/documentation",
979 "HotlistDocumentation" },
980 { "https://www.netsurf-browser.org/contact",
981 "HotlistContact" }
982 };
983 const int n_entries = sizeof(default_entries) /
984 sizeof(default_entries[0]);
985
986 /* First make "NetSurf" folder for defualt entries */
987 title = "NetSurf";
988 err = hotlist_add_folder_internal(title, NULL,
989 TREE_REL_FIRST_CHILD, &f, false);
990 if (err != NSERROR_OK) {
991 return err;
992 }
993
994 /* And add entries as children of folder node */
995 for (i = n_entries - 1; i >= 0; i--) {
996 /* Get URL as nsurl object */
997 err = nsurl_create(default_entries[i].url, &url);
998 if (err != NSERROR_OK) {
999 return NSERROR_NOMEM;
1000 }
1001
1002 title = messages_get(default_entries[i].msg_key);
1003
1004 /* Build the node */
1005 err = hotlist_add_entry_internal(url, title,
1006 NULL, f->folder, TREE_REL_FIRST_CHILD, &e);
1007 nsurl_unref(url);
1008
1009 if (err != NSERROR_OK) {
1010 return NSERROR_NOMEM;
1011 }
1012 }
1013
1014 return NSERROR_OK;
1015}
1016
1017
1019 FILE *fp;
1020};
1021
1022/** Callback for treeview_walk node entering */
1023static nserror hotlist_export_enter_cb(void *ctx, void *node_data,
1024 enum treeview_node_type type, bool *abort)
1025{
1026 struct treeview_export_walk_ctx *tw = ctx;
1027 nserror ret;
1028
1029 if (type == TREE_NODE_ENTRY) {
1030 struct hotlist_entry *e = node_data;
1031 char *t_text;
1032 char *u_text;
1033
1034 ret = utf8_to_html(e->data[HL_TITLE].value, "iso-8859-1",
1035 e->data[HL_TITLE].value_len, &t_text);
1036 if (ret != NSERROR_OK)
1037 return NSERROR_SAVE_FAILED;
1038
1039 ret = utf8_to_html(e->data[HL_URL].value, "iso-8859-1",
1040 e->data[HL_URL].value_len, &u_text);
1041 if (ret != NSERROR_OK) {
1042 free(t_text);
1043 return NSERROR_SAVE_FAILED;
1044 }
1045
1046 fprintf(tw->fp, "<li><a href=\"%s\">%s</a></li>\n",
1047 u_text, t_text);
1048
1049 free(t_text);
1050 free(u_text);
1051
1052 } else if (type == TREE_NODE_FOLDER) {
1053 struct hotlist_folder *f = node_data;
1054 char *f_text;
1055
1056 ret = utf8_to_html(f->data.value, "iso-8859-1",
1057 f->data.value_len, &f_text);
1058 if (ret != NSERROR_OK)
1059 return NSERROR_SAVE_FAILED;
1060
1061 if (f == hl_ctx.default_folder) {
1062 fprintf(tw->fp, "<h4>%s</h4>\n<ul id=\"default\">\n",
1063 f_text);
1064 } else {
1065 fprintf(tw->fp, "<h4>%s</h4>\n<ul>\n", f_text);
1066 }
1067
1068 free(f_text);
1069 }
1070
1071 return NSERROR_OK;
1072}
1073/** Callback for treeview_walk node leaving */
1074static nserror hotlist_export_leave_cb(void *ctx, void *node_data,
1075 enum treeview_node_type type, bool *abort)
1076{
1077 struct treeview_export_walk_ctx *tw = ctx;
1078
1079 if (type == TREE_NODE_FOLDER) {
1080 fputs("</ul>\n", tw->fp);
1081 }
1082
1083 return NSERROR_OK;
1084}
1085/* Exported interface, documented in hotlist.h */
1086nserror hotlist_export(const char *path, const char *title)
1087{
1088 struct treeview_export_walk_ctx tw;
1089 nserror err;
1090 FILE *fp;
1091
1092 fp = fopen(path, "w");
1093 if (fp == NULL)
1094 return NSERROR_SAVE_FAILED;
1095
1096 if (title == NULL)
1097 title = "NetSurf hotlist";
1098
1099 /* The Acorn Browse Hotlist format, which we mimic[*], is invalid HTML
1100 * claming to be valid.
1101 * [*] Why? */
1102 fputs("<!DOCTYPE html "
1103 "PUBLIC \"//W3C/DTD HTML 4.01//EN\" "
1104 "\"http://www.w3.org/TR/html4/strict.dtd\">\n", fp);
1105 fputs("<html>\n<head>\n", fp);
1106 fputs("<meta http-equiv=\"Content-Type\" "
1107 "content=\"text/html; charset=iso-8859-1\">\n", fp);
1108 fprintf(fp, "<title>%s</title>\n", title);
1109 fputs("</head>\n<body>\n<ul>\n", fp);
1110
1111 tw.fp = fp;
1112 err = treeview_walk(hl_ctx.tree, NULL,
1116 if (err != NSERROR_OK)
1117 return err;
1118
1119 fputs("</ul>\n</body>\n</html>\n", fp);
1120
1121 fclose(fp);
1122
1123 return NSERROR_OK;
1124}
1125
1126
1131 void *ctx;
1132};
1133/** Callback for hotlist_iterate node entering */
1134static nserror hotlist_iterate_enter_cb(void *ctx, void *node_data,
1135 enum treeview_node_type type, bool *abort)
1136{
1137 struct hotlist_iterate_ctx *data = ctx;
1138
1139 if (type == TREE_NODE_ENTRY && data->address_cb != NULL) {
1140 struct hotlist_entry *e = node_data;
1141 data->address_cb(data->ctx, e->url,
1142 e->data[HL_TITLE].value);
1143
1144 } else if (type == TREE_NODE_FOLDER && data->enter_cb != NULL) {
1145 struct hotlist_folder *f = node_data;
1146 data->enter_cb(data->ctx, f->data.value);
1147 }
1148
1149 return NSERROR_OK;
1150}
1151/** Callback for hotlist_iterate node leaving */
1152static nserror hotlist_iterate_leave_cb(void *ctx, void *node_data,
1153 enum treeview_node_type type, bool *abort)
1154{
1155 struct hotlist_iterate_ctx *data = ctx;
1156
1157 if (type == TREE_NODE_FOLDER && data->leave_cb != NULL) {
1158 data->leave_cb(data->ctx);
1159 }
1160
1161 return NSERROR_OK;
1162}
1163/* Exported interface, documented in hotlist.h */
1168{
1169 struct hotlist_iterate_ctx data;
1170 nserror err;
1171
1172 data.enter_cb = enter_cb;
1173 data.address_cb = address_cb;
1174 data.leave_cb = leave_cb;
1175 data.ctx = ctx;
1176
1177 err = treeview_walk(hl_ctx.tree, NULL,
1181 if (err != NSERROR_OK)
1182 return err;
1183
1184 return NSERROR_OK;
1185}
1186
1187
1188/**
1189 * Initialise the treeview entry feilds
1190 *
1191 * \return NSERROR_OK on success, or appropriate error otherwise
1192 */
1194{
1195 int i;
1196 const char *label;
1197
1198 for (i = 0; i < HL_N_FIELDS; i++)
1199 hl_ctx.fields[i].field = NULL;
1200
1203 label = "TreeviewLabelTitle";
1204 label = messages_get(label);
1205 if (lwc_intern_string(label, strlen(label),
1207 lwc_error_ok) {
1208 goto error;
1209 }
1210
1215 label = "TreeviewLabelURL";
1216 label = messages_get(label);
1217 if (lwc_intern_string(label, strlen(label),
1219 lwc_error_ok) {
1220 goto error;
1221 }
1222
1224 label = "TreeviewLabelLastVisit";
1225 label = messages_get(label);
1226 if (lwc_intern_string(label, strlen(label),
1228 lwc_error_ok) {
1229 goto error;
1230 }
1231
1233 label = "TreeviewLabelVisits";
1234 label = messages_get(label);
1235 if (lwc_intern_string(label, strlen(label),
1237 lwc_error_ok) {
1238 goto error;
1239 }
1240
1243 label = "TreeviewLabelFolder";
1244 label = messages_get(label);
1245 if (lwc_intern_string(label, strlen(label),
1247 lwc_error_ok) {
1248 return false;
1249 }
1250
1251 return NSERROR_OK;
1252
1253error:
1254 for (i = 0; i < HL_N_FIELDS; i++)
1255 if (hl_ctx.fields[i].field != NULL)
1256 lwc_string_unref(hl_ctx.fields[i].field);
1257
1258 return NSERROR_UNKNOWN;
1259}
1260
1261
1262/*
1263 * Populate the hotlist from file, or generate default hotlist if no file
1264 *
1265 * \param path The path to load the hotlist file from, or NULL
1266 * \return NSERROR_OK on success, or appropriate error otherwise
1267 */
1268static nserror hotlist_populate(const char *path)
1269{
1270 nserror err;
1271 bool loaded;
1272
1273 /* Load hotlist file */
1274 err = hotlist_load(path, &loaded);
1275
1276 if (loaded && err == NSERROR_OK)
1277 return err;
1278
1279 /* Couldn't load hotlist, generate a default one */
1280 err = hotlist_generate();
1281 if (err != NSERROR_OK) {
1282 return err;
1283 }
1284
1285 return NSERROR_OK;
1286}
1287
1288
1289/* Exported interface, documented in hotlist.h */
1291 const char *load_path,
1292 const char *save_path)
1293{
1294 nserror err;
1295
1296 err = treeview_init();
1297 if (err != NSERROR_OK) {
1298 return err;
1299 }
1300
1301 NSLOG(netsurf, INFO, "Loading hotlist");
1302
1303 hl_ctx.tree = NULL;
1304 hl_ctx.built = false;
1305 hl_ctx.default_folder = NULL;
1306
1307 /* Store the save path */
1308 if (save_path != NULL) {
1309 hl_ctx.save_path = strdup(save_path);
1310 if (hl_ctx.save_path == NULL) {
1311 return NSERROR_NOMEM;
1312 }
1313 } else {
1314 hl_ctx.save_path = NULL;
1315 }
1316
1317 /* Init. hotlist treeview entry fields */
1319 if (err != NSERROR_OK) {
1320 free(hl_ctx.save_path);
1321 hl_ctx.tree = NULL;
1322 return err;
1323 }
1324
1325 /* Create the hotlist treeview */
1327 HL_N_FIELDS, hl_ctx.fields, NULL,
1329 if (err != NSERROR_OK) {
1330 free(hl_ctx.save_path);
1331 hl_ctx.tree = NULL;
1332 return err;
1333 }
1334
1335 /* Populate the hotlist */
1336 err = hotlist_populate(load_path);
1337 if (err != NSERROR_OK) {
1338 free(hl_ctx.save_path);
1339 return err;
1340 }
1341
1342 /* Hotlist tree is built
1343 * We suppress the treeview height callback on entry insertion before
1344 * the treeview is built. */
1345 hl_ctx.built = true;
1346
1347 NSLOG(netsurf, INFO, "Loaded hotlist");
1348
1349 return NSERROR_OK;
1350}
1351
1352
1353/* Exported interface, documented in hotlist.h */
1354nserror hotlist_manager_init(void *core_window_handle)
1355{
1356 nserror err;
1357
1358 /* Create the hotlist treeview */
1359 err = treeview_cw_attach(hl_ctx.tree, core_window_handle);
1360 if (err != NSERROR_OK) {
1361 return err;
1362 }
1363
1364 /* Inform client of window height */
1366
1367 return NSERROR_OK;
1368}
1369
1370
1371/* Exported interface, documented in hotlist.h */
1373{
1374 nserror err;
1375
1376 /* Create the hotlist treeview */
1378 if (err != NSERROR_OK) {
1379 return err;
1380 }
1381
1382 return NSERROR_OK;
1383}
1384
1385
1386/* Exported interface, documented in hotlist.h */
1388{
1389 int i;
1390 nserror err;
1391
1392 NSLOG(netsurf, INFO, "Finalising hotlist");
1393
1394 /* Remove any existing scheduled save callback */
1396 hl_ctx.save_scheduled = false;
1397
1398 /* Save the hotlist */
1400 if (err != NSERROR_OK) {
1401 NSLOG(netsurf, INFO, "Problem saving the hotlist.");
1402 }
1403
1404 free(hl_ctx.save_path);
1405
1406 /* Destroy the hotlist treeview */
1408 if (err != NSERROR_OK) {
1409 NSLOG(netsurf, INFO, "Problem destroying the hotlist treeview.");
1410 }
1411 hl_ctx.built = false;
1412
1413 /* Free hotlist treeview entry fields */
1414 for (i = 0; i < HL_N_FIELDS; i++)
1415 if (hl_ctx.fields[i].field != NULL)
1416 lwc_string_unref(hl_ctx.fields[i].field);
1417
1418 err = treeview_fini();
1419 if (err != NSERROR_OK) {
1420 return err;
1421 }
1422
1423 NSLOG(netsurf, INFO, "Finalised hotlist");
1424
1425 return err;
1426}
1427
1428
1429/* Exported interface, documented in hotlist.h */
1431{
1432 treeview_node *entry;
1433 nserror err;
1434
1435 /* If we don't have a hotlist at the moment, just return OK */
1436 if (hl_ctx.tree == NULL)
1437 return NSERROR_OK;
1438
1439 /* Make the default folder, if we don't have one */
1440 if (hl_ctx.default_folder == NULL) {
1441 const char *temp = messages_get("HotlistDefaultFolderName");
1442 struct hotlist_folder *f;
1443 err = hotlist_add_folder_internal(temp, NULL,
1444 TREE_REL_FIRST_CHILD, &f, true);
1445 if (err != NSERROR_OK)
1446 return err;
1447
1448 if (f == NULL)
1449 return NSERROR_NOMEM;
1450
1452 }
1453
1454 /* Add new entry to default folder */
1455 err = hotlist_add_entry_internal(url, NULL, NULL,
1457 TREE_REL_FIRST_CHILD, &entry);
1458 if (err != NSERROR_OK)
1459 return err;
1460
1461 /* Ensure default folder is expanded */
1463 if (err != NSERROR_OK)
1464 return err;
1465
1466 return hotlist_schedule_save();
1467}
1468
1469
1472 bool found;
1473};
1474/** Callback for treeview_walk */
1475static nserror hotlist_has_url_walk_cb(void *ctx, void *node_data,
1476 enum treeview_node_type type, bool *abort)
1477{
1478 struct treeview_has_url_walk_ctx *tw = ctx;
1479
1480 if (type == TREE_NODE_ENTRY) {
1481 struct hotlist_entry *e = node_data;
1482
1483 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1484 /* Found what we're looking for */
1485 tw->found = true;
1486 *abort = true;
1487 }
1488 }
1489
1490 return NSERROR_OK;
1491}
1492/* Exported interface, documented in hotlist.h */
1494{
1495 nserror err;
1496 struct treeview_has_url_walk_ctx tw = {
1497 .url = url,
1498 .found = false
1499 };
1500
1501 if (hl_ctx.built == false)
1502 return false;
1503
1505 &tw, TREE_NODE_ENTRY);
1506 if (err != NSERROR_OK)
1507 return false;
1508
1509 return tw.found;
1510}
1511
1512
1515};
1516/** Callback for treeview_walk */
1517static nserror hotlist_remove_url_walk_cb(void *ctx, void *node_data,
1518 enum treeview_node_type type, bool *abort)
1519{
1520 struct treeview_remove_url_walk_ctx *tw = ctx;
1521
1522 if (type == TREE_NODE_ENTRY) {
1523 struct hotlist_entry *e = node_data;
1524
1525 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1526 /* Found what we're looking for: delete it */
1529 }
1530 }
1531
1532 return NSERROR_OK;
1533}
1534/* Exported interface, documented in hotlist.h */
1536{
1537 nserror err;
1538 struct treeview_remove_url_walk_ctx tw = {
1539 .url = url
1540 };
1541
1542 if (hl_ctx.built == false)
1543 return;
1544
1546 &tw, TREE_NODE_ENTRY);
1547 if (err != NSERROR_OK)
1548 return;
1549
1550 return;
1551}
1552
1553
1556 const struct url_data *data;
1557};
1558/** Callback for treeview_walk */
1559static nserror hotlist_update_url_walk_cb(void *ctx, void *node_data,
1560 enum treeview_node_type type, bool *abort)
1561{
1562 struct treeview_update_url_walk_ctx *tw = ctx;
1563 struct hotlist_entry *e = node_data;
1564 nserror err;
1565
1566 if (type != TREE_NODE_ENTRY) {
1567 return NSERROR_OK;
1568 }
1569
1570 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1571 /* Found match: Update the entry data */
1572 free((void *)e->data[HL_LAST_VISIT].value); /* Eww */
1573 free((void *)e->data[HL_VISITS].value); /* Eww */
1574
1575 if (tw->data == NULL) {
1576 /* Get the URL data */
1577 tw->data = urldb_get_url_data(tw->url);
1578 if (tw->data == NULL) {
1579 /* No entry in database, so add one */
1580 urldb_add_url(tw->url);
1581 /* now attempt to get url data */
1582 tw->data = urldb_get_url_data(tw->url);
1583 }
1584 if (tw->data == NULL) {
1585 return NSERROR_NOMEM;
1586 }
1587 }
1588
1590 if (err != NSERROR_OK)
1591 return err;
1592
1594 e->entry, e->data, e);
1595 if (err != NSERROR_OK)
1596 return err;
1597 }
1598
1599 return NSERROR_OK;
1600}
1601/* Exported interface, documented in hotlist.h */
1603{
1604 nserror err;
1605 struct treeview_update_url_walk_ctx tw = {
1606 .url = url,
1607 .data = NULL
1608 };
1609
1610 if (hl_ctx.built == false)
1611 return;
1612
1614 &tw, TREE_NODE_ENTRY);
1615 if (err != NSERROR_OK)
1616 return;
1617
1618 return;
1619}
1620
1621
1622/* Exported interface, documented in hotlist.h */
1623nserror hotlist_add_entry(nsurl *url, const char *title, bool at_y, int y)
1624{
1625 nserror err;
1626 treeview_node *entry;
1627 treeview_node *relation;
1628 enum treeview_relationship rel;
1629
1630 if (url == NULL) {
1631 err = nsurl_create("https://netsurf-browser.org/", &url);
1632 if (err != NSERROR_OK) {
1633 return err;
1634 }
1635 assert(url != NULL);
1636 } else {
1637 nsurl_ref(url);
1638 }
1639
1640 err = treeview_get_relation(hl_ctx.tree, &relation, &rel, at_y, y);
1641 if (err != NSERROR_OK) {
1643 return err;
1644 }
1645
1646 err = hotlist_add_entry_internal(url, title, NULL,
1647 relation, rel, &entry);
1648 if (err != NSERROR_OK) {
1650 return err;
1651 }
1652
1654
1655 return NSERROR_OK;
1656}
1657
1658
1659/* Exported interface, documented in hotlist.h */
1660nserror hotlist_add_folder(const char *title, bool at_y, int y)
1661{
1662 nserror err;
1663 struct hotlist_folder *f;
1664 treeview_node *relation;
1665 enum treeview_relationship rel;
1666
1667 err = treeview_get_relation(hl_ctx.tree, &relation, &rel, at_y, y);
1668 if (err != NSERROR_OK) {
1669 return err;
1670 }
1671
1672 err = hotlist_add_folder_internal(title, relation, rel, &f, false);
1673 if (err != NSERROR_OK) {
1674 return err;
1675 }
1676
1677 return NSERROR_OK;
1678}
1679
1680
1681/* Exported interface, documented in hotlist.h */
1682void hotlist_redraw(int x, int y, struct rect *clip,
1683 const struct redraw_context *ctx)
1684{
1685 treeview_redraw(hl_ctx.tree, x, y, clip, ctx);
1686}
1687
1688
1689/* Exported interface, documented in hotlist.h */
1691{
1692 treeview_mouse_action(hl_ctx.tree, mouse, x, y);
1693}
1694
1695
1696/* Exported interface, documented in hotlist.h */
1697bool hotlist_keypress(uint32_t key)
1698{
1699 return treeview_keypress(hl_ctx.tree, key);
1700}
1701
1702
1703/* Exported interface, documented in hotlist.h */
1705{
1707}
1708
1709
1710/* Exported interface, documented in hotlist.h */
1711bool hotlist_get_selection(nsurl **url, const char **title)
1712{
1713 struct hotlist_entry *e;
1715 void *v;
1716
1718 if (type != TREE_NODE_ENTRY || v == NULL) {
1719 *url = NULL;
1720 *title = NULL;
1721 return false;
1722 }
1723
1724 e = (struct hotlist_entry *)v;
1725
1726 *url = e->url;
1727 *title = e->data[HL_TITLE].value;
1728 return true;
1729}
1730
1731
1732/* Exported interface, documented in hotlist.h */
1734{
1736}
1737
1738
1739/* Exported interface, documented in hotlist.h */
1740nserror hotlist_expand(bool only_folders)
1741{
1742 return treeview_expand(hl_ctx.tree, only_folders);
1743}
1744
1745
1746/* Exported interface, documented in hotlist.h */
1748{
1749 return treeview_contract(hl_ctx.tree, all);
1750}
1751
Browser window creation and manipulation interface.
nserror browser_window_create(enum browser_window_create_flags flags, struct nsurl *url, struct nsurl *referrer, struct browser_window *existing, struct browser_window **bw)
Create and open a new root browser window with the given page.
browser_window_create_flags
flags to browser_window_create
@ BW_CREATE_HISTORY
this will form a new history node (don't set for back/reload/etc)
static osspriteop_area * buffer
The buffer characteristics.
Definition: buffer.c:55
Useful interned string pointers (interface).
static nserror hotlist_update_url_walk_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for treeview_walk.
Definition: hotlist.c:1559
bool hotlist_keypress(uint32_t key)
Key press handling.
Definition: hotlist.c:1697
static nserror hotlist_export_enter_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for treeview_walk node entering.
Definition: hotlist.c:1023
void hotlist_update_url(nsurl *url)
Update given URL, e.g.
Definition: hotlist.c:1602
bool hotlist_has_selection(void)
Determine whether there is a selection.
Definition: hotlist.c:1704
nserror hotlist_add_entry(nsurl *url, const char *title, bool at_y, int y)
Add an entry to the hotlist for given Title/URL.
Definition: hotlist.c:1623
struct treeview_callback_table hl_tree_cb_t
Definition: hotlist.c:624
void hotlist_mouse_action(browser_mouse_state mouse, int x, int y)
Handles all kinds of mouse action.
Definition: hotlist.c:1690
static nserror hotlist_load_entry(dom_node *li, hotlist_load_ctx *ctx)
Parse an entry represented as a li.
Definition: hotlist.c:647
nserror hotlist_fini(void)
Finalise the hotlist.
Definition: hotlist.c:1387
nserror hotlist_add_url(nsurl *url)
Add an entry to the hotlist for given URL.
Definition: hotlist.c:1430
nserror hotlist_manager_init(void *core_window_handle)
Initialise the hotlist manager.
Definition: hotlist.c:1354
hotlist_fields
Definition: hotlist.c:46
@ HL_TITLE
Definition: hotlist.c:47
@ HL_URL
Definition: hotlist.c:48
@ HL_VISITS
Definition: hotlist.c:50
@ HL_FOLDER
Definition: hotlist.c:51
@ HL_LAST_VISIT
Definition: hotlist.c:49
@ HL_N_FIELDS
Definition: hotlist.c:52
static nserror hotlist_add_folder_internal(const char *title, treeview_node *relation, enum treeview_relationship rel, struct hotlist_folder **folder, bool default_folder)
Add folder to the hotlist (creates the folder).
Definition: hotlist.c:443
static nserror hotlist_iterate_enter_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for hotlist_iterate node entering.
Definition: hotlist.c:1134
static nserror hotlist_add_entry_internal(nsurl *url, const char *title, const struct url_data *data, treeview_node *relation, enum treeview_relationship rel, treeview_node **entry)
Add an entry to the hotlist (creates the entry).
Definition: hotlist.c:406
static nserror hotlist_create_treeview_field_data(struct hotlist_entry *e, const char *title, const struct url_data *data)
Set a hotlist entry's data from the url_data.
Definition: hotlist.c:251
static nserror hotlist_load_directory(dom_node *ul, hotlist_load_ctx *ctx)
Parse a directory represented as a ul.
Definition: hotlist.c:733
static nserror hotlist_has_url_walk_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for treeview_walk.
Definition: hotlist.c:1475
static nserror hotlist_populate(const char *path)
Definition: hotlist.c:1268
nserror hotlist_manager_fini(void)
Finalise the hotlist manager.
Definition: hotlist.c:1372
void hotlist_redraw(int x, int y, struct rect *clip, const struct redraw_context *ctx)
Redraw the hotlist.
Definition: hotlist.c:1682
static nserror hotlist_tree_node_folder_cb(struct treeview_node_msg msg, void *data)
Definition: hotlist.c:491
static nserror hotlist_save(const char *path)
Definition: hotlist.c:113
nserror hotlist_iterate(void *ctx, hotlist_folder_enter_cb enter_cb, hotlist_address_cb address_cb, hotlist_folder_leave_cb leave_cb)
Walk (depth first) the hotlist, calling callbacks on entering folders, address nodes,...
Definition: hotlist.c:1164
static nserror hotlist_export_leave_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for treeview_walk node leaving.
Definition: hotlist.c:1074
static void hotlist_delete_entry_internal(struct hotlist_entry *e)
Delete a hotlist entry.
Definition: hotlist.c:324
static nserror hotlist_schedule_save(void)
Schedule a hotlist save.
Definition: hotlist.c:170
static nserror hotlist_remove_url_walk_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for treeview_walk.
Definition: hotlist.c:1517
static nserror hotlist_create_treeview_field_visits_data(struct hotlist_entry *e, const struct url_data *data)
Set a hotlist entry's data from the url_data.
Definition: hotlist.c:192
nserror hotlist_add_folder(const char *title, bool at_y, int y)
Add a folder to the hotlist.
Definition: hotlist.c:1660
nserror hotlist_init(const char *load_path, const char *save_path)
Initialise the hotlist.
Definition: hotlist.c:1290
nserror hotlist_export(const char *path, const char *title)
Save hotlist to file.
Definition: hotlist.c:1086
static nserror hotlist_load(const char *path, bool *loaded)
Definition: hotlist.c:858
nserror hotlist_expand(bool only_folders)
Expand the treeview's nodes.
Definition: hotlist.c:1740
static nserror hotlist_initialise_entry_fields(void)
Initialise the treeview entry feilds.
Definition: hotlist.c:1193
static nserror hotlist_generate(void)
Definition: hotlist.c:962
static nserror hotlist_create_entry(nsurl *url, const char *title, const struct url_data *data, struct hotlist_entry **entry)
Create hotlist entry data for URL.
Definition: hotlist.c:349
static nserror hotlist_load_directory_cb(dom_node *node, void *ctx)
Definition: hotlist.c:744
void hotlist_edit_selection(void)
Edit the first selected node.
Definition: hotlist.c:1733
static nserror hotlist_get_temp_path(const char *path, char **temp_path)
Definition: hotlist.c:85
bool hotlist_has_url(nsurl *url)
Check whether given URL is present in hotlist.
Definition: hotlist.c:1493
static void hotlist_schedule_save_cb(void *p)
Scheduler callback for saving the hotlist.
Definition: hotlist.c:158
struct hotlist_ctx hl_ctx
Definition: hotlist.c:68
static nserror hotlist_entry_insert(struct hotlist_entry *e, treeview_node *relation, enum treeview_relationship rel)
Add a hotlist entry to the treeview.
Definition: hotlist.c:299
bool hotlist_get_selection(nsurl **url, const char **title)
Get the first selected node.
Definition: hotlist.c:1711
void hotlist_remove_url(nsurl *url)
Remove any entries matching the given URL from the hotlist.
Definition: hotlist.c:1535
static nserror hotlist_iterate_leave_cb(void *ctx, void *node_data, enum treeview_node_type type, bool *abort)
Callback for hotlist_iterate node leaving.
Definition: hotlist.c:1152
nserror hotlist_contract(bool all)
Contract the treeview's nodes.
Definition: hotlist.c:1747
static nserror hotlist_tree_node_entry_cb(struct treeview_node_msg msg, void *data)
callback for hotlist treeview entry manipulation.
Definition: hotlist.c:538
nserror(* hotlist_address_cb)(void *ctx, struct nsurl *url, const char *title)
Client callback for hotlist_iterate, reporting a hotlist address.
Definition: hotlist.h:163
nserror(* hotlist_folder_leave_cb)(void *ctx)
Client callback for hotlist_iterate, reporting a hotlist folder departure.
Definition: hotlist.h:172
nserror(* hotlist_folder_enter_cb)(void *ctx, const char *title)
Client callback for hotlist_iterate, reporting entry into folder.
Definition: hotlist.h:153
nserror treeview_create(treeview **treeout, const struct treeview_callback_table *callbacks, int n_fields, struct treeview_field_desc fields[], struct core_window *cw, treeview_flags flags)
Create a treeview.
Definition: treeview.c:2022
nserror treeview_cw_detach(treeview *tree)
Detach a treeview from a corewindow.
Definition: treeview.c:2147
nserror treeview_cw_attach(treeview *tree, struct core_window *cw)
Attach a treeview to a corewindow.
Definition: treeview.c:2132
void treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
Handles all kinds of mouse action.
Definition: treeview.c:4723
nserror treeview_contract(treeview *tree, bool all)
Contract a treeview's nodes.
Definition: treeview.c:2434
nserror treeview_delete_node(treeview *tree, treeview_node *n, treeview_node_options_flags flags)
Delete a treeview node.
Definition: treeview.c:1924
enum treeview_node_type treeview_get_selection(treeview *tree, void **node_data)
Get the first selected node.
Definition: treeview.c:3363
nserror treeview_fini(void)
Finalise the treeview module (all treeviews must have been destroyed first)
Definition: treeview.c:5384
void treeview_edit_selection(treeview *tree)
Edit the first selected node.
Definition: treeview.c:4389
bool treeview_has_selection(treeview *tree)
Determine whether treeview has a selection.
Definition: treeview.c:3326
bool treeview_keypress(treeview *tree, uint32_t key)
Key press handling for treeviews.
Definition: treeview.c:4006
nserror treeview_update_node_entry(treeview *tree, treeview_node *entry, const struct treeview_field_data fields[], void *data)
Update an entry node in given treeview.
Definition: treeview.c:1314
nserror treeview_get_relation(treeview *tree, treeview_node **relation, enum treeview_relationship *rel, bool at_y, int y)
Find a relation for node creation.
Definition: treeview.c:3776
nserror treeview_create_node_folder(treeview *tree, treeview_node **folder, treeview_node *relation, enum treeview_relationship rel, const struct treeview_field_data *field, void *data, treeview_node_options_flags flags)
Create a folder node in given treeview.
Definition: treeview.c:1198
nserror treeview_destroy(treeview *tree)
Destroy a treeview object.
Definition: treeview.c:2158
nserror treeview_walk(treeview *tree, treeview_node *root, treeview_walk_cb enter_cb, treeview_walk_cb leave_cb, void *ctx, enum treeview_node_type type)
Walk (depth first) a treeview subtree, calling a callback at each node of required type.
Definition: treeview.c:1534
int treeview_get_height(treeview *tree)
Find current height of a treeview.
Definition: treeview.c:4908
void treeview_redraw(treeview *tree, const int x, const int y, struct rect *clip, const struct redraw_context *ctx)
Redraw a treeview object.
Definition: treeview.c:2997
nserror treeview_node_expand(treeview *tree, treeview_node *node)
Expand a treeview node.
Definition: treeview.c:2291
nserror treeview_init(void)
Prepare treeview module for treeview usage.
Definition: treeview.c:5335
nserror treeview_create_node_entry(treeview *tree, treeview_node **entry, treeview_node *relation, enum treeview_relationship rel, const struct treeview_field_data fields[], void *data, treeview_node_options_flags flags)
Create an entry node in given treeview.
Definition: treeview.c:1388
nserror treeview_expand(treeview *tree, bool only_folders)
Expand a treeview's nodes.
Definition: treeview.c:2525
nserror treeview_update_node_folder(treeview *tree, treeview_node *folder, const struct treeview_field_data *field, void *data)
Update an folder node in given treeview.
Definition: treeview.c:1266
Treeview handling interface.
@ TREE_MSG_NODE_EDIT
Node to be edited.
Definition: treeview.h:87
@ TREE_MSG_NODE_LAUNCH
Node to be launched.
Definition: treeview.h:88
@ TREE_MSG_NODE_DELETE
Node to be deleted.
Definition: treeview.h:86
treeview_node_type
treeview node type
Definition: treeview.h:43
@ TREE_NODE_ENTRY
Node is an entry.
Definition: treeview.h:47
@ TREE_NODE_FOLDER
Node is folder.
Definition: treeview.h:46
treeview_node_options_flags
Node change handling options.
Definition: treeview.h:63
@ TREE_OPTION_NONE
Definition: treeview.h:64
@ TREE_OPTION_SUPPRESS_RESIZE
Definition: treeview.h:66
@ TREE_OPTION_SPECIAL_DIR
Definition: treeview.h:65
@ TREE_OPTION_SUPPRESS_REDRAW
Definition: treeview.h:67
@ TREE_FLAG_SEARCHABLE
Whether field is searchable.
Definition: treeview.h:121
@ TREE_FLAG_SHOW_NAME
Whether field name shown.
Definition: treeview.h:119
@ TREE_FLAG_COPY_TEXT
Whether to copy to clipb.
Definition: treeview.h:120
@ TREE_FLAG_ALLOW_EDIT
Whether allow edit field.
Definition: treeview.h:117
@ TREE_FLAG_DEFAULT
Whether field is default.
Definition: treeview.h:118
treeview_relationship
Relationship between nodes.
Definition: treeview.h:54
@ TREE_REL_FIRST_CHILD
Definition: treeview.h:55
@ TREE_REL_NEXT_SIBLING
Definition: treeview.h:56
@ TREEVIEW_SEARCHABLE
Treeview has search bar.
Definition: treeview.h:79
nserror
Enumeration of error codes.
Definition: errors.h:29
@ NSERROR_SAVE_FAILED
Failed to save data.
Definition: errors.h:36
@ NSERROR_DOM
DOM call returned error.
Definition: errors.h:52
@ NSERROR_UNKNOWN
Unknown error - DO NOT USE.
Definition: errors.h:31
@ NSERROR_INVALID
Invalid data.
Definition: errors.h:49
@ NSERROR_NOMEM
Memory exhaustion.
Definition: errors.h:32
@ NSERROR_OK
No error.
Definition: errors.h:30
const char * type
Definition: filetype.cpp:44
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:50
Interface to core interface table.
Interface to platform-specific miscellaneous browser operation table.
browser_mouse_state
Mouse state: 1 is primary mouse button.
Definition: mouse.h:52
@ BROWSER_MOUSE_MOD_2
2nd modifier key pressed (eg.
Definition: mouse.h:101
@ BROWSER_MOUSE_MOD_1
1st modifier key pressed (eg.
Definition: mouse.h:99
nserror libdom_iterate_child_elements(dom_node *parent, libdom_iterate_cb cb, void *ctx)
Definition: libdom.c:76
dom_node * libdom_find_first_element(dom_node *parent, lwc_string *element_name)
Search children of a node for first named element.
Definition: libdom.c:33
nserror libdom_parse_file(const char *filename, const char *encoding, dom_document **doc)
Definition: libdom.c:381
libdom utilities (implementation).
#define NSLOG(catname, level, logmsg, args...)
Definition: log.h:116
const char * messages_get(const char *key)
Fast lookup of a message by key from the standard Messages hash.
Definition: messages.c:241
Localised message support (interface).
NetSurf URL handling (interface).
bool nsurl_compare(const nsurl *url1, const nsurl *url2, nsurl_component parts)
Compare two URLs.
nserror nsurl_create(const char *const url_s, nsurl **url)
Create a NetSurf URL object from a URL string.
void nsurl_unref(nsurl *url)
Drop a reference to a NetSurf URL object.
const char * nsurl_access(const nsurl *url)
Access a NetSurf URL object as a string.
size_t nsurl_length(const nsurl *url)
Find the length of a NetSurf URL object's URL, as returned by nsurl_access.
nsurl * nsurl_ref(nsurl *url)
Increment the reference count to a NetSurf URL object.
@ NSURL_COMPLETE
Definition: nsurl.h:54
struct nsurl nsurl
NetSurf URL object.
Definition: nsurl.h:31
Interface to utility string handling.
Browser window data.
nserror(* schedule)(int t, void(*callback)(void *p), void *p)
Schedule a callback.
Definition: misc.h:58
struct treeview_field_desc fields[HL_N_FIELDS]
Definition: hotlist.c:62
treeview * tree
Definition: hotlist.c:61
struct hotlist_folder * default_folder
Definition: hotlist.c:64
bool save_scheduled
Definition: hotlist.c:66
char * save_path
Definition: hotlist.c:65
bool built
Definition: hotlist.c:63
Definition: hotlist.c:70
treeview_node * entry
Definition: hotlist.c:72
nsurl * url
Definition: hotlist.c:71
struct treeview_field_data data[HL_N_FIELDS - 1]
Definition: hotlist.c:74
struct treeview_field_data data
Definition: hotlist.c:57
treeview_node * folder
Definition: hotlist.c:56
hotlist_address_cb address_cb
Definition: hotlist.c:1129
hotlist_folder_leave_cb leave_cb
Definition: hotlist.c:1130
hotlist_folder_enter_cb enter_cb
Definition: hotlist.c:1128
treeview * tree
Definition: hotlist.c:632
enum treeview_relationship relshp
Definition: hotlist.c:634
bool last_was_h4
Definition: hotlist.c:635
dom_string * title
Definition: hotlist.c:636
treeview_node * rel
Definition: hotlist.c:633
struct gui_misc_table * misc
Browser table.
Definition: gui_table.h:57
Rectangle coordinates.
Definition: types.h:40
Redraw context.
Definition: plotters.h:51
Client callbacks for events concerning nodes.
Definition: treeview.h:147
nserror(* folder)(struct treeview_node_msg msg, void *data)
Definition: treeview.h:148
Treeview field data.
Definition: treeview.h:137
const char * value
Field value.
Definition: treeview.h:139
lwc_string * field
Field name.
Definition: treeview.h:138
size_t value_len
Field value length (bytes)
Definition: treeview.h:140
Treeview field description.
Definition: treeview.h:128
lwc_string * field
A treeview field name.
Definition: treeview.h:129
enum treeview_field_flags flags
Flags for field.
Definition: treeview.h:130
treeview message
Definition: treeview.h:95
lwc_string * field
The field being edited.
Definition: treeview.h:102
union treeview_node_msg::@94 data
The message data.
const char * text
The proposed new value.
Definition: treeview.h:103
browser_mouse_state mouse
Definition: treeview.h:106
struct treeview_node_msg::@94::@97 node_launch
struct treeview_node_msg::@94::@96 node_edit
enum treeview_msg msg
The message type.
Definition: treeview.h:96
Treeview node.
Definition: treeview.c:133
const struct url_data * data
Definition: hotlist.c:1556
The treeview context.
Definition: treeview.c:232
unsigned int visits
Visit count.
Definition: url_db.h:38
time_t last_visit
Last visit time.
Definition: url_db.h:39
const char * title
Resource title.
Definition: url_db.h:37
const struct url_data * urldb_get_url_data(struct nsurl *url)
Find data for an URL.
Definition: urldb.c:3309
nserror urldb_set_url_persistence(nsurl *url, bool persist)
Set the cross-session persistence of the entry for an URL.
Definition: urldb.c:3122
bool urldb_add_url(nsurl *url)
Insert an URL into the database.
Definition: urldb.c:3140
Unified URL information database internal interface.
nserror utf8_to_html(const char *string, const char *encname, size_t len, char **result_out)
Convert a UTF-8 encoded string into a string of the given encoding, applying HTML escape sequences wh...
Definition: utf8.c:369
UTF-8 manipulation functions (interface).
Interface to a number of general purpose functionality.
static nserror path(const struct redraw_context *ctx, const plot_style_t *pstyle, const float *p, unsigned int n, const float transform[6])
Plots a path.
Definition: plot.c:821
static nserror clip(const struct redraw_context *ctx, const struct rect *clip)
Sets a clip rectangle for subsequent plot operations.
Definition: plot.c:357