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 lwc_string_unref(hl_ctx.fields[i].field);
1256
1257 return NSERROR_UNKNOWN;
1258}
1259
1260
1261/*
1262 * Populate the hotlist from file, or generate default hotlist if no file
1263 *
1264 * \param path The path to load the hotlist file from, or NULL
1265 * \return NSERROR_OK on success, or appropriate error otherwise
1266 */
1267static nserror hotlist_populate(const char *path)
1268{
1269 nserror err;
1270 bool loaded;
1271
1272 /* Load hotlist file */
1273 err = hotlist_load(path, &loaded);
1274
1275 if (loaded && err == NSERROR_OK)
1276 return err;
1277
1278 /* Couldn't load hotlist, generate a default one */
1279 err = hotlist_generate();
1280 if (err != NSERROR_OK) {
1281 return err;
1282 }
1283
1284 return NSERROR_OK;
1285}
1286
1287
1288/* Exported interface, documented in hotlist.h */
1290 const char *load_path,
1291 const char *save_path)
1292{
1293 nserror err;
1294
1295 err = treeview_init();
1296 if (err != NSERROR_OK) {
1297 return err;
1298 }
1299
1300 NSLOG(netsurf, INFO, "Loading hotlist");
1301
1302 hl_ctx.tree = NULL;
1303 hl_ctx.built = false;
1304 hl_ctx.default_folder = NULL;
1305
1306 /* Store the save path */
1307 if (save_path != NULL) {
1308 hl_ctx.save_path = strdup(save_path);
1309 if (hl_ctx.save_path == NULL) {
1310 return NSERROR_NOMEM;
1311 }
1312 } else {
1313 hl_ctx.save_path = NULL;
1314 }
1315
1316 /* Init. hotlist treeview entry fields */
1318 if (err != NSERROR_OK) {
1319 free(hl_ctx.save_path);
1320 hl_ctx.tree = NULL;
1321 return err;
1322 }
1323
1324 /* Create the hotlist treeview */
1326 HL_N_FIELDS, hl_ctx.fields, NULL,
1328 if (err != NSERROR_OK) {
1329 free(hl_ctx.save_path);
1330 hl_ctx.tree = NULL;
1331 return err;
1332 }
1333
1334 /* Populate the hotlist */
1335 err = hotlist_populate(load_path);
1336 if (err != NSERROR_OK) {
1337 free(hl_ctx.save_path);
1338 return err;
1339 }
1340
1341 /* Hotlist tree is built
1342 * We suppress the treeview height callback on entry insertion before
1343 * the treeview is built. */
1344 hl_ctx.built = true;
1345
1346 NSLOG(netsurf, INFO, "Loaded hotlist");
1347
1348 return NSERROR_OK;
1349}
1350
1351
1352/* Exported interface, documented in hotlist.h */
1353nserror hotlist_manager_init(void *core_window_handle)
1354{
1355 nserror err;
1356
1357 /* Create the hotlist treeview */
1358 err = treeview_cw_attach(hl_ctx.tree, core_window_handle);
1359 if (err != NSERROR_OK) {
1360 return err;
1361 }
1362
1363 /* Inform client of window height */
1365
1366 return NSERROR_OK;
1367}
1368
1369
1370/* Exported interface, documented in hotlist.h */
1372{
1373 nserror err;
1374
1375 /* Create the hotlist treeview */
1377 if (err != NSERROR_OK) {
1378 return err;
1379 }
1380
1381 return NSERROR_OK;
1382}
1383
1384
1385/* Exported interface, documented in hotlist.h */
1387{
1388 int i;
1389 nserror err;
1390
1391 NSLOG(netsurf, INFO, "Finalising hotlist");
1392
1393 /* Remove any existing scheduled save callback */
1395 hl_ctx.save_scheduled = false;
1396
1397 /* Save the hotlist */
1399 if (err != NSERROR_OK) {
1400 NSLOG(netsurf, INFO, "Problem saving the hotlist.");
1401 }
1402
1403 free(hl_ctx.save_path);
1404
1405 /* Destroy the hotlist treeview */
1407 if (err != NSERROR_OK) {
1408 NSLOG(netsurf, INFO, "Problem destroying the hotlist treeview.");
1409 }
1410 hl_ctx.built = false;
1411
1412 /* Free hotlist treeview entry fields */
1413 for (i = 0; i < HL_N_FIELDS; i++)
1414 lwc_string_unref(hl_ctx.fields[i].field);
1415
1416 err = treeview_fini();
1417 if (err != NSERROR_OK) {
1418 return err;
1419 }
1420
1421 NSLOG(netsurf, INFO, "Finalised hotlist");
1422
1423 return err;
1424}
1425
1426
1427/* Exported interface, documented in hotlist.h */
1429{
1430 treeview_node *entry;
1431 nserror err;
1432
1433 /* If we don't have a hotlist at the moment, just return OK */
1434 if (hl_ctx.tree == NULL)
1435 return NSERROR_OK;
1436
1437 /* Make the default folder, if we don't have one */
1438 if (hl_ctx.default_folder == NULL) {
1439 const char *temp = messages_get("HotlistDefaultFolderName");
1440 struct hotlist_folder *f;
1441 err = hotlist_add_folder_internal(temp, NULL,
1442 TREE_REL_FIRST_CHILD, &f, true);
1443 if (err != NSERROR_OK)
1444 return err;
1445
1446 if (f == NULL)
1447 return NSERROR_NOMEM;
1448
1450 }
1451
1452 /* Add new entry to default folder */
1453 err = hotlist_add_entry_internal(url, NULL, NULL,
1455 TREE_REL_FIRST_CHILD, &entry);
1456 if (err != NSERROR_OK)
1457 return err;
1458
1459 /* Ensure default folder is expanded */
1461 if (err != NSERROR_OK)
1462 return err;
1463
1464 return hotlist_schedule_save();
1465}
1466
1467
1470 bool found;
1471};
1472/** Callback for treeview_walk */
1473static nserror hotlist_has_url_walk_cb(void *ctx, void *node_data,
1474 enum treeview_node_type type, bool *abort)
1475{
1476 struct treeview_has_url_walk_ctx *tw = ctx;
1477
1478 if (type == TREE_NODE_ENTRY) {
1479 struct hotlist_entry *e = node_data;
1480
1481 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1482 /* Found what we're looking for */
1483 tw->found = true;
1484 *abort = true;
1485 }
1486 }
1487
1488 return NSERROR_OK;
1489}
1490/* Exported interface, documented in hotlist.h */
1492{
1493 nserror err;
1494 struct treeview_has_url_walk_ctx tw = {
1495 .url = url,
1496 .found = false
1497 };
1498
1499 if (hl_ctx.built == false)
1500 return false;
1501
1503 &tw, TREE_NODE_ENTRY);
1504 if (err != NSERROR_OK)
1505 return false;
1506
1507 return tw.found;
1508}
1509
1510
1513};
1514/** Callback for treeview_walk */
1515static nserror hotlist_remove_url_walk_cb(void *ctx, void *node_data,
1516 enum treeview_node_type type, bool *abort)
1517{
1518 struct treeview_remove_url_walk_ctx *tw = ctx;
1519
1520 if (type == TREE_NODE_ENTRY) {
1521 struct hotlist_entry *e = node_data;
1522
1523 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1524 /* Found what we're looking for: delete it */
1527 }
1528 }
1529
1530 return NSERROR_OK;
1531}
1532/* Exported interface, documented in hotlist.h */
1534{
1535 nserror err;
1536 struct treeview_remove_url_walk_ctx tw = {
1537 .url = url
1538 };
1539
1540 if (hl_ctx.built == false)
1541 return;
1542
1544 &tw, TREE_NODE_ENTRY);
1545 if (err != NSERROR_OK)
1546 return;
1547
1548 return;
1549}
1550
1551
1554 const struct url_data *data;
1555};
1556/** Callback for treeview_walk */
1557static nserror hotlist_update_url_walk_cb(void *ctx, void *node_data,
1558 enum treeview_node_type type, bool *abort)
1559{
1560 struct treeview_update_url_walk_ctx *tw = ctx;
1561 struct hotlist_entry *e = node_data;
1562 nserror err;
1563
1564 if (type != TREE_NODE_ENTRY) {
1565 return NSERROR_OK;
1566 }
1567
1568 if (nsurl_compare(e->url, tw->url, NSURL_COMPLETE) == true) {
1569 /* Found match: Update the entry data */
1570 free((void *)e->data[HL_LAST_VISIT].value); /* Eww */
1571 free((void *)e->data[HL_VISITS].value); /* Eww */
1572
1573 if (tw->data == NULL) {
1574 /* Get the URL data */
1575 tw->data = urldb_get_url_data(tw->url);
1576 if (tw->data == NULL) {
1577 /* No entry in database, so add one */
1578 urldb_add_url(tw->url);
1579 /* now attempt to get url data */
1580 tw->data = urldb_get_url_data(tw->url);
1581 }
1582 if (tw->data == NULL) {
1583 return NSERROR_NOMEM;
1584 }
1585 }
1586
1588 if (err != NSERROR_OK)
1589 return err;
1590
1592 e->entry, e->data, e);
1593 if (err != NSERROR_OK)
1594 return err;
1595 }
1596
1597 return NSERROR_OK;
1598}
1599/* Exported interface, documented in hotlist.h */
1601{
1602 nserror err;
1603 struct treeview_update_url_walk_ctx tw = {
1604 .url = url,
1605 .data = NULL
1606 };
1607
1608 if (hl_ctx.built == false)
1609 return;
1610
1612 &tw, TREE_NODE_ENTRY);
1613 if (err != NSERROR_OK)
1614 return;
1615
1616 return;
1617}
1618
1619
1620/* Exported interface, documented in hotlist.h */
1621nserror hotlist_add_entry(nsurl *url, const char *title, bool at_y, int y)
1622{
1623 nserror err;
1624 treeview_node *entry;
1625 treeview_node *relation;
1626 enum treeview_relationship rel;
1627
1628 if (url == NULL) {
1629 err = nsurl_create("https://netsurf-browser.org/", &url);
1630 if (err != NSERROR_OK) {
1631 return err;
1632 }
1633 assert(url != NULL);
1634 } else {
1635 nsurl_ref(url);
1636 }
1637
1638 err = treeview_get_relation(hl_ctx.tree, &relation, &rel, at_y, y);
1639 if (err != NSERROR_OK) {
1641 return err;
1642 }
1643
1644 err = hotlist_add_entry_internal(url, title, NULL,
1645 relation, rel, &entry);
1646 if (err != NSERROR_OK) {
1648 return err;
1649 }
1650
1652
1653 return NSERROR_OK;
1654}
1655
1656
1657/* Exported interface, documented in hotlist.h */
1658nserror hotlist_add_folder(const char *title, bool at_y, int y)
1659{
1660 nserror err;
1661 struct hotlist_folder *f;
1662 treeview_node *relation;
1663 enum treeview_relationship rel;
1664
1665 err = treeview_get_relation(hl_ctx.tree, &relation, &rel, at_y, y);
1666 if (err != NSERROR_OK) {
1667 return err;
1668 }
1669
1670 err = hotlist_add_folder_internal(title, relation, rel, &f, false);
1671 if (err != NSERROR_OK) {
1672 return err;
1673 }
1674
1675 return NSERROR_OK;
1676}
1677
1678
1679/* Exported interface, documented in hotlist.h */
1680void hotlist_redraw(int x, int y, struct rect *clip,
1681 const struct redraw_context *ctx)
1682{
1683 treeview_redraw(hl_ctx.tree, x, y, clip, ctx);
1684}
1685
1686
1687/* Exported interface, documented in hotlist.h */
1689{
1690 treeview_mouse_action(hl_ctx.tree, mouse, x, y);
1691}
1692
1693
1694/* Exported interface, documented in hotlist.h */
1695bool hotlist_keypress(uint32_t key)
1696{
1697 return treeview_keypress(hl_ctx.tree, key);
1698}
1699
1700
1701/* Exported interface, documented in hotlist.h */
1703{
1705}
1706
1707
1708/* Exported interface, documented in hotlist.h */
1709bool hotlist_get_selection(nsurl **url, const char **title)
1710{
1711 struct hotlist_entry *e;
1713 void *v;
1714
1716 if (type != TREE_NODE_ENTRY || v == NULL) {
1717 *url = NULL;
1718 *title = NULL;
1719 return false;
1720 }
1721
1722 e = (struct hotlist_entry *)v;
1723
1724 *url = e->url;
1725 *title = e->data[HL_TITLE].value;
1726 return true;
1727}
1728
1729
1730/* Exported interface, documented in hotlist.h */
1732{
1734}
1735
1736
1737/* Exported interface, documented in hotlist.h */
1738nserror hotlist_expand(bool only_folders)
1739{
1740 return treeview_expand(hl_ctx.tree, only_folders);
1741}
1742
1743
1744/* Exported interface, documented in hotlist.h */
1746{
1747 return treeview_contract(hl_ctx.tree, all);
1748}
1749
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:1557
bool hotlist_keypress(uint32_t key)
Key press handling.
Definition: hotlist.c:1695
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:1600
bool hotlist_has_selection(void)
Determine whether there is a selection.
Definition: hotlist.c:1702
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:1621
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:1688
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:1386
nserror hotlist_add_url(nsurl *url)
Add an entry to the hotlist for given URL.
Definition: hotlist.c:1428
nserror hotlist_manager_init(void *core_window_handle)
Initialise the hotlist manager.
Definition: hotlist.c:1353
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:1473
static nserror hotlist_populate(const char *path)
Definition: hotlist.c:1267
nserror hotlist_manager_fini(void)
Finalise the hotlist manager.
Definition: hotlist.c:1371
void hotlist_redraw(int x, int y, struct rect *clip, const struct redraw_context *ctx)
Redraw the hotlist.
Definition: hotlist.c:1680
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:1515
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:1658
nserror hotlist_init(const char *load_path, const char *save_path)
Initialise the hotlist.
Definition: hotlist.c:1289
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:1738
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:1731
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:1491
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:1709
void hotlist_remove_url(nsurl *url)
Remove any entries matching the given URL from the hotlist.
Definition: hotlist.c:1533
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:1745
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:2024
nserror treeview_cw_detach(treeview *tree)
Detach a treeview from a corewindow.
Definition: treeview.c:2149
nserror treeview_cw_attach(treeview *tree, struct core_window *cw)
Attach a treeview to a corewindow.
Definition: treeview.c:2134
void treeview_mouse_action(treeview *tree, browser_mouse_state mouse, int x, int y)
Handles all kinds of mouse action.
Definition: treeview.c:4725
nserror treeview_contract(treeview *tree, bool all)
Contract a treeview's nodes.
Definition: treeview.c:2436
nserror treeview_delete_node(treeview *tree, treeview_node *n, treeview_node_options_flags flags)
Delete a treeview node.
Definition: treeview.c:1926
enum treeview_node_type treeview_get_selection(treeview *tree, void **node_data)
Get the first selected node.
Definition: treeview.c:3365
nserror treeview_fini(void)
Finalise the treeview module (all treeviews must have been destroyed first)
Definition: treeview.c:5389
void treeview_edit_selection(treeview *tree)
Edit the first selected node.
Definition: treeview.c:4391
bool treeview_has_selection(treeview *tree)
Determine whether treeview has a selection.
Definition: treeview.c:3328
bool treeview_keypress(treeview *tree, uint32_t key)
Key press handling for treeviews.
Definition: treeview.c:4008
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:1316
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:3778
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:1200
nserror treeview_destroy(treeview *tree)
Destroy a treeview object.
Definition: treeview.c:2160
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:1536
int treeview_get_height(treeview *tree)
Find current height of a treeview.
Definition: treeview.c:4910
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:2999
nserror treeview_node_expand(treeview *tree, treeview_node *node)
Expand a treeview node.
Definition: treeview.c:2293
nserror treeview_init(void)
Prepare treeview module for treeview usage.
Definition: treeview.c:5340
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:1390
nserror treeview_expand(treeview *tree, bool only_folders)
Expand a treeview's nodes.
Definition: treeview.c:2527
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:1268
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:256
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:1554
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:3305
nserror urldb_set_url_persistence(nsurl *url, bool persist)
Set the cross-session persistence of the entry for an URL.
Definition: urldb.c:3119
bool urldb_add_url(nsurl *url)
Insert an URL into the database.
Definition: urldb.c:3137
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