File: | desktop/global_history.c |
Warning: | line 809, column 2 Value stored to 'err' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright 2012 - 2013 Michael Drake <tlsa@netsurf-browser.org> |
3 | * |
4 | * This file is part of NetSurf, http://www.netsurf-browser.org/ |
5 | * |
6 | * NetSurf is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; version 2 of the License. |
9 | * |
10 | * NetSurf is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | |
20 | #include <stdlib.h> |
21 | #include <string.h> |
22 | #include <time.h> |
23 | |
24 | #include "utils/messages.h" |
25 | #include "utils/utils.h" |
26 | #include "utils/utf8.h" |
27 | #include "utils/libdom.h" |
28 | #include "utils/log.h" |
29 | #include "utils/nsurl.h" |
30 | #include "content/urldb.h" |
31 | |
32 | #include "desktop/global_history.h" |
33 | #include "desktop/treeview.h" |
34 | #include "netsurf/browser_window.h" |
35 | |
36 | #define N_DAYS28 28 |
37 | #define N_SEC_PER_DAY(60 * 60 * 24) (60 * 60 * 24) |
38 | |
39 | enum global_history_folders { |
40 | GH_TODAY = 0, |
41 | GH_YESTERDAY, |
42 | GH_2_DAYS_AGO, |
43 | GH_3_DAYS_AGO, |
44 | GH_4_DAYS_AGO, |
45 | GH_5_DAYS_AGO, |
46 | GH_6_DAYS_AGO, |
47 | GH_LAST_WEEK, |
48 | GH_2_WEEKS_AGO, |
49 | GH_3_WEEKS_AGO, |
50 | GH_N_FOLDERS |
51 | }; |
52 | |
53 | enum global_history_fields { |
54 | GH_TITLE, |
55 | GH_URL, |
56 | GH_LAST_VISIT, |
57 | GH_VISITS, |
58 | GH_PERIOD, |
59 | N_FIELDS |
60 | }; |
61 | |
62 | struct global_history_folder { |
63 | treeview_node *folder; |
64 | struct treeview_field_data data; |
65 | }; |
66 | |
67 | struct global_history_ctx { |
68 | treeview *tree; |
69 | struct treeview_field_desc fields[N_FIELDS]; |
70 | struct global_history_folder folders[GH_N_FOLDERS]; |
71 | time_t today; |
72 | int weekday; |
73 | bool_Bool built; |
74 | }; |
75 | struct global_history_ctx gh_ctx; |
76 | |
77 | struct global_history_entry { |
78 | bool_Bool user_delete; |
79 | |
80 | int slot; |
81 | nsurl *url; |
82 | time_t t; |
83 | treeview_node *entry; |
84 | struct global_history_entry *next; |
85 | struct global_history_entry *prev; |
86 | |
87 | struct treeview_field_data data[N_FIELDS - 1]; |
88 | }; |
89 | struct global_history_entry *gh_list[N_DAYS28]; |
90 | |
91 | |
92 | /** |
93 | * Find an entry in the global history |
94 | * |
95 | * \param url The URL to find |
96 | * \return Pointer to history entry, or NULL if not found |
97 | */ |
98 | static struct global_history_entry *global_history_find(nsurl *url) |
99 | { |
100 | int i; |
101 | struct global_history_entry *e; |
102 | |
103 | for (i = 0; i < N_DAYS28; i++) { |
104 | e = gh_list[i]; |
105 | |
106 | while (e != NULL((void*)0)) { |
107 | if (nsurl_compare(e->url, url, |
108 | NSURL_COMPLETE) == true1) { |
109 | /* Got a match */ |
110 | return e; |
111 | } |
112 | e = e->next; |
113 | } |
114 | |
115 | } |
116 | |
117 | /* No match found */ |
118 | return NULL((void*)0); |
119 | } |
120 | |
121 | |
122 | /** |
123 | * Initialise the treeview directories |
124 | * |
125 | * \param f Ident for folder to create |
126 | * \return NSERROR_OK on success, appropriate error otherwise |
127 | */ |
128 | static nserror global_history_create_dir(enum global_history_folders f) |
129 | { |
130 | nserror err; |
131 | treeview_node *relation = NULL((void*)0); |
132 | enum treeview_relationship rel = TREE_REL_FIRST_CHILD; |
133 | const char *label; |
134 | int i; |
135 | |
136 | switch (f) { |
137 | case GH_TODAY: |
138 | label = "DateToday"; |
139 | break; |
140 | |
141 | case GH_YESTERDAY: |
142 | label = "DateYesterday"; |
143 | break; |
144 | |
145 | case GH_2_DAYS_AGO: |
146 | label = "Date2Days"; |
147 | break; |
148 | |
149 | case GH_3_DAYS_AGO: |
150 | label = "Date3Days"; |
151 | break; |
152 | |
153 | case GH_4_DAYS_AGO: |
154 | label = "Date4Days"; |
155 | break; |
156 | |
157 | case GH_5_DAYS_AGO: |
158 | label = "Date5Days"; |
159 | break; |
160 | |
161 | case GH_6_DAYS_AGO: |
162 | label = "Date6Days"; |
163 | break; |
164 | |
165 | case GH_LAST_WEEK: |
166 | label = "Date1Week"; |
167 | break; |
168 | |
169 | case GH_2_WEEKS_AGO: |
170 | label = "Date2Week"; |
171 | break; |
172 | |
173 | case GH_3_WEEKS_AGO: |
174 | label = "Date3Week"; |
175 | break; |
176 | |
177 | default: |
178 | return NSERROR_BAD_PARAMETER; |
179 | } |
180 | |
181 | label = messages_get(label); |
182 | |
183 | for (i = f - 1; i >= 0; i--) { |
184 | if (gh_ctx.folders[i].folder != NULL((void*)0)) { |
185 | relation = gh_ctx.folders[i].folder; |
186 | rel = TREE_REL_NEXT_SIBLING; |
187 | break; |
188 | } |
189 | } |
190 | |
191 | gh_ctx.folders[f].data.field = gh_ctx.fields[N_FIELDS - 1].field; |
192 | gh_ctx.folders[f].data.value = label; |
193 | gh_ctx.folders[f].data.value_len = strlen(label); |
194 | err = treeview_create_node_folder(gh_ctx.tree, |
195 | &gh_ctx.folders[f].folder, |
196 | relation, rel, |
197 | &gh_ctx.folders[f].data, |
198 | &gh_ctx.folders[f], |
199 | gh_ctx.built ? TREE_OPTION_NONE : |
200 | TREE_OPTION_SUPPRESS_RESIZE | |
201 | TREE_OPTION_SUPPRESS_REDRAW); |
202 | |
203 | return err; |
204 | } |
205 | |
206 | |
207 | /** |
208 | * Get the treeview folder for history entires in a particular slot |
209 | * |
210 | * \param parent Updated to parent folder. |
211 | * \param slot Global history slot of entry we want folder node for |
212 | * \return NSERROR_OK on success, appropriate error otherwise |
213 | */ |
214 | static inline nserror global_history_get_parent_treeview_node( |
215 | treeview_node **parent, int slot) |
216 | { |
217 | int folder_index; |
218 | struct global_history_folder *f; |
219 | nserror err; |
220 | |
221 | if (slot < 7) { |
222 | folder_index = slot; |
223 | |
224 | } else if (slot < 14) { |
225 | folder_index = GH_LAST_WEEK; |
226 | |
227 | } else if (slot < 21) { |
228 | folder_index = GH_2_WEEKS_AGO; |
229 | |
230 | } else if (slot < N_DAYS28) { |
231 | folder_index = GH_3_WEEKS_AGO; |
232 | |
233 | } else { |
234 | /* Slot value is invalid */ |
235 | return NSERROR_BAD_PARAMETER; |
236 | } |
237 | |
238 | /* Get the folder */ |
239 | f = &(gh_ctx.folders[folder_index]); |
240 | |
241 | if (f->folder == NULL((void*)0)) { |
242 | err = global_history_create_dir(folder_index); |
243 | if (err != NSERROR_OK) { |
244 | *parent = NULL((void*)0); |
245 | return err; |
246 | } |
247 | } |
248 | |
249 | /* Return the parent treeview folder */ |
250 | *parent = f->folder; |
251 | return NSERROR_OK; |
252 | } |
253 | |
254 | |
255 | /** |
256 | * Set a global history entry's data from the url_data. |
257 | * |
258 | * \param e Global history entry to set up |
259 | * \param data Data associated with entry's URL |
260 | * \return NSERROR_OK on success, appropriate error otherwise |
261 | */ |
262 | static nserror global_history_create_treeview_field_data( |
263 | struct global_history_entry *e, |
264 | const struct url_data *data) |
265 | { |
266 | const char *title = (data->title != NULL((void*)0)) ? |
267 | data->title : messages_get("NoTitle"); |
268 | char buffer[16]; |
269 | struct tm *lvtime; |
270 | char *last_visited = NULL((void*)0); |
271 | size_t len = 0; |
272 | |
273 | e->data[GH_TITLE].field = gh_ctx.fields[GH_TITLE].field; |
274 | e->data[GH_TITLE].value = strdup(title); |
275 | e->data[GH_TITLE].value_len = (e->data[GH_TITLE].value != NULL((void*)0)) ? |
276 | strlen(title) : 0; |
277 | |
278 | e->data[GH_URL].field = gh_ctx.fields[GH_URL].field; |
279 | e->data[GH_URL].value = nsurl_access(e->url); |
280 | e->data[GH_URL].value_len = nsurl_length(e->url); |
281 | |
282 | if ((lvtime = localtime(&data->last_visit)) != NULL((void*)0)) { |
283 | const size_t lvsize = 256; |
284 | last_visited = malloc(lvsize); |
285 | if (last_visited != NULL((void*)0)) { |
286 | len = strftime(last_visited, lvsize, |
287 | "%a %b %e %H:%M:%S %Y", lvtime); |
288 | } |
289 | } |
290 | |
291 | e->data[GH_LAST_VISIT].field = gh_ctx.fields[GH_LAST_VISIT].field; |
292 | e->data[GH_LAST_VISIT].value = last_visited; |
293 | e->data[GH_LAST_VISIT].value_len = len; |
294 | |
295 | len = snprintf(buffer, 16, "%u", data->visits); |
296 | if (len == 16) { |
297 | len--; |
298 | buffer[len] = '\0'; |
299 | } |
300 | |
301 | e->data[GH_VISITS].field = gh_ctx.fields[GH_VISITS].field; |
302 | e->data[GH_VISITS].value = strdup(buffer); |
303 | e->data[GH_VISITS].value_len = len; |
304 | |
305 | return NSERROR_OK; |
306 | } |
307 | |
308 | /** |
309 | * Add a global history entry to the treeview |
310 | * |
311 | * \param e entry to add to treeview |
312 | * \param slot global history slot containing entry |
313 | * \return NSERROR_OK on success, or appropriate error otherwise |
314 | * |
315 | * It is assumed that the entry is unique (for its URL) in the global |
316 | * history table |
317 | */ |
318 | static nserror global_history_entry_insert(struct global_history_entry *e, |
319 | int slot) |
320 | { |
321 | nserror err; |
322 | |
323 | treeview_node *parent; |
324 | err = global_history_get_parent_treeview_node(&parent, slot); |
325 | if (err != NSERROR_OK) { |
326 | return err; |
327 | } |
328 | |
329 | err = treeview_create_node_entry(gh_ctx.tree, &(e->entry), |
330 | parent, TREE_REL_FIRST_CHILD, e->data, e, |
331 | gh_ctx.built ? TREE_OPTION_NONE : |
332 | TREE_OPTION_SUPPRESS_RESIZE | |
333 | TREE_OPTION_SUPPRESS_REDRAW); |
334 | if (err != NSERROR_OK) { |
335 | return err; |
336 | } |
337 | |
338 | return NSERROR_OK; |
339 | } |
340 | |
341 | |
342 | /** |
343 | * Add an entry to the global history (creates the entry). |
344 | * |
345 | * If the treeview has already been created, the entry will be added to the |
346 | * treeview. Otherwise, the entry will have to be added to the treeview later. |
347 | * |
348 | * When we first create the global history we create it without the treeview, to |
349 | * simplfy sorting the entries. |
350 | * |
351 | * \param url URL for entry to add to history |
352 | * \param slot Global history slot to contain history entry |
353 | * \param data URL data for the entry |
354 | * \param got_treeview Whether the treeview has been created already |
355 | * \return NSERROR_OK on success, or appropriate error otherwise |
356 | */ |
357 | static nserror global_history_add_entry_internal(nsurl *url, int slot, |
358 | const struct url_data *data, bool_Bool got_treeview) |
359 | { |
360 | nserror err; |
361 | struct global_history_entry *e; |
362 | |
363 | /* Create new local history entry */ |
364 | e = malloc(sizeof(struct global_history_entry)); |
365 | if (e == NULL((void*)0)) { |
366 | return NSERROR_NOMEM; |
367 | } |
368 | |
369 | e->user_delete = false0; |
370 | e->slot = slot; |
371 | e->url = nsurl_ref(url); |
372 | e->t = data->last_visit; |
373 | e->entry = NULL((void*)0); |
374 | e->next = NULL((void*)0); |
375 | e->prev = NULL((void*)0); |
376 | |
377 | err = global_history_create_treeview_field_data(e, data); |
378 | if (err != NSERROR_OK) { |
379 | return err; |
380 | } |
381 | |
382 | if (gh_list[slot] == NULL((void*)0)) { |
383 | /* list empty */ |
384 | gh_list[slot] = e; |
385 | |
386 | } else if (gh_list[slot]->t < e->t) { |
387 | /* Insert at list head */ |
388 | e->next = gh_list[slot]; |
389 | gh_list[slot]->prev = e; |
390 | gh_list[slot] = e; |
391 | } else { |
392 | struct global_history_entry *prev = gh_list[slot]; |
393 | struct global_history_entry *curr = prev->next; |
394 | while (curr != NULL((void*)0)) { |
395 | if (curr->t < e->t) { |
396 | break; |
397 | } |
398 | prev = curr; |
399 | curr = curr->next; |
400 | } |
401 | |
402 | /* insert after prev */ |
403 | e->next = curr; |
404 | e->prev = prev; |
405 | prev->next = e; |
406 | |
407 | if (curr != NULL((void*)0)) |
408 | curr->prev = e; |
409 | } |
410 | |
411 | if (got_treeview) { |
412 | err = global_history_entry_insert(e, slot); |
413 | if (err != NSERROR_OK) { |
414 | return err; |
415 | } |
416 | } |
417 | |
418 | return NSERROR_OK; |
419 | } |
420 | |
421 | |
422 | /** |
423 | * Delete a global history entry |
424 | * |
425 | * This does not delete the treeview node, rather it should only be called from |
426 | * the treeview node delete event message. |
427 | * |
428 | * \param e Entry to delete |
429 | */ |
430 | static void global_history_delete_entry_internal( |
431 | struct global_history_entry *e) |
432 | { |
433 | assert(e != NULL)((e != ((void*)0)) ? (void) (0) : __assert_fail ("e != NULL", "desktop/global_history.c", 433, __extension__ __PRETTY_FUNCTION__ )); |
434 | assert(e->entry == NULL)((e->entry == ((void*)0)) ? (void) (0) : __assert_fail ("e->entry == NULL" , "desktop/global_history.c", 434, __extension__ __PRETTY_FUNCTION__ )); |
435 | |
436 | /* Unlink */ |
437 | if (gh_list[e->slot] == e) { |
438 | /* e is first entry */ |
439 | gh_list[e->slot] = e->next; |
440 | |
441 | if (e->next != NULL((void*)0)) |
442 | e->next->prev = NULL((void*)0); |
443 | |
444 | } else if (e->next == NULL((void*)0)) { |
445 | /* e is last entry */ |
446 | e->prev->next = NULL((void*)0); |
447 | |
448 | } else { |
449 | /* e has an entry before and after */ |
450 | e->prev->next = e->next; |
451 | e->next->prev = e->prev; |
452 | } |
453 | |
454 | if (e->user_delete) { |
455 | /* User requested delete, so delete from urldb too. */ |
456 | urldb_reset_url_visit_data(e->url); |
457 | } |
458 | |
459 | /* Destroy fields */ |
460 | free((void *)e->data[GH_TITLE].value); /* Eww */ |
461 | free((void *)e->data[GH_LAST_VISIT].value); /* Eww */ |
462 | free((void *)e->data[GH_VISITS].value); /* Eww */ |
463 | nsurl_unref(e->url); |
464 | |
465 | /* Destroy entry */ |
466 | free(e); |
467 | } |
468 | |
469 | /** |
470 | * Internal routine to actually perform global history addition |
471 | * |
472 | * \param url The URL to add |
473 | * \param data URL data associated with URL |
474 | * \return true (for urldb_iterate_entries) |
475 | */ |
476 | static bool_Bool global_history_add_entry(nsurl *url, |
477 | const struct url_data *data) |
478 | { |
479 | int slot; |
480 | time_t visit_date; |
481 | time_t earliest_date = gh_ctx.today - (N_DAYS28 - 1) * N_SEC_PER_DAY(60 * 60 * 24); |
482 | bool_Bool got_treeview = gh_ctx.tree != NULL((void*)0); |
483 | |
484 | assert((url != NULL) && (data != NULL))(((url != ((void*)0)) && (data != ((void*)0))) ? (void ) (0) : __assert_fail ("(url != NULL) && (data != NULL)" , "desktop/global_history.c", 484, __extension__ __PRETTY_FUNCTION__ )); |
485 | |
486 | visit_date = data->last_visit; |
487 | |
488 | /* Find day array slot for entry */ |
489 | if (visit_date >= gh_ctx.today) { |
490 | slot = 0; |
491 | } else if (visit_date >= earliest_date) { |
492 | slot = (gh_ctx.today - visit_date) / N_SEC_PER_DAY(60 * 60 * 24) + 1; |
493 | } else { |
494 | /* too old */ |
495 | return true1; |
496 | } |
497 | |
498 | if (got_treeview == true1) { |
499 | /* The treeview for global history already exists */ |
500 | struct global_history_entry *e; |
501 | |
502 | /* Delete any existing entry for this URL */ |
503 | e = global_history_find(url); |
504 | if (e != NULL((void*)0)) { |
505 | treeview_delete_node(gh_ctx.tree, e->entry, |
506 | TREE_OPTION_SUPPRESS_REDRAW | |
507 | TREE_OPTION_SUPPRESS_RESIZE); |
508 | } |
509 | } |
510 | |
511 | if (global_history_add_entry_internal(url, slot, data, |
512 | got_treeview) != NSERROR_OK) { |
513 | return false0; |
514 | } |
515 | |
516 | return true1; |
517 | } |
518 | |
519 | /** |
520 | * Initialise the treeview entry feilds |
521 | * |
522 | * \return NSERROR_OK on success, or appropriate error otherwise |
523 | */ |
524 | static nserror global_history_initialise_entry_fields(void) |
525 | { |
526 | int i; |
527 | const char *label; |
528 | |
529 | for (i = 0; i < N_FIELDS; i++) |
530 | gh_ctx.fields[i].field = NULL((void*)0); |
531 | |
532 | gh_ctx.fields[GH_TITLE].flags = TREE_FLAG_DEFAULT; |
533 | label = "TreeviewLabelTitle"; |
534 | label = messages_get(label); |
535 | if (lwc_intern_string(label, strlen(label), |
536 | &gh_ctx.fields[GH_TITLE].field) != |
537 | lwc_error_ok) { |
538 | goto error; |
539 | } |
540 | |
541 | gh_ctx.fields[GH_URL].flags = |
542 | TREE_FLAG_COPY_TEXT | |
543 | TREE_FLAG_SEARCHABLE; |
544 | label = "TreeviewLabelURL"; |
545 | label = messages_get(label); |
546 | if (lwc_intern_string(label, strlen(label), |
547 | &gh_ctx.fields[GH_URL].field) != |
548 | lwc_error_ok) { |
549 | goto error; |
550 | } |
551 | |
552 | gh_ctx.fields[GH_LAST_VISIT].flags = TREE_FLAG_SHOW_NAME; |
553 | label = "TreeviewLabelLastVisit"; |
554 | label = messages_get(label); |
555 | if (lwc_intern_string(label, strlen(label), |
556 | &gh_ctx.fields[GH_LAST_VISIT].field) != |
557 | lwc_error_ok) { |
558 | goto error; |
559 | } |
560 | |
561 | gh_ctx.fields[GH_VISITS].flags = TREE_FLAG_SHOW_NAME; |
562 | label = "TreeviewLabelVisits"; |
563 | label = messages_get(label); |
564 | if (lwc_intern_string(label, strlen(label), |
565 | &gh_ctx.fields[GH_VISITS].field) != |
566 | lwc_error_ok) { |
567 | goto error; |
568 | } |
569 | |
570 | gh_ctx.fields[GH_PERIOD].flags = TREE_FLAG_DEFAULT; |
571 | label = "TreeviewLabelPeriod"; |
572 | label = messages_get(label); |
573 | if (lwc_intern_string(label, strlen(label), |
574 | &gh_ctx.fields[GH_PERIOD].field) != |
575 | lwc_error_ok) { |
576 | return false0; |
577 | } |
578 | |
579 | return NSERROR_OK; |
580 | |
581 | error: |
582 | for (i = 0; i < N_FIELDS; i++) |
583 | if (gh_ctx.fields[i].field != NULL((void*)0)) |
584 | lwc_string_unref(gh_ctx.fields[i].field){ lwc_string *__lwc_s = (gh_ctx.fields[i].field); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "desktop/global_history.c", 584, __extension__ __PRETTY_FUNCTION__ )); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); }; |
585 | |
586 | return NSERROR_UNKNOWN; |
587 | } |
588 | |
589 | |
590 | /** |
591 | * Initialise the time |
592 | * |
593 | * \return NSERROR_OK on success, or appropriate error otherwise |
594 | */ |
595 | static nserror global_history_initialise_time(void) |
596 | { |
597 | struct tm *full_time; |
598 | time_t t; |
599 | |
600 | /* get the current time */ |
601 | t = time(NULL((void*)0)); |
602 | if (t == -1) { |
603 | NSLOG(netsurf, INFO, "time info unaviable")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 603 , }; nslog__log(&_nslog_ctx, "time info unaviable"); } } while (0); |
604 | return NSERROR_UNKNOWN; |
605 | } |
606 | |
607 | /* get the time at the start of today */ |
608 | full_time = localtime(&t); |
609 | full_time->tm_sec = 0; |
610 | full_time->tm_min = 0; |
611 | full_time->tm_hour = 0; |
612 | t = mktime(full_time); |
613 | if (t == -1) { |
614 | NSLOG(netsurf, INFO, "mktime failed")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 614 , }; nslog__log(&_nslog_ctx, "mktime failed"); } } while( 0); |
615 | return NSERROR_UNKNOWN; |
616 | } |
617 | |
618 | gh_ctx.today = t; |
619 | gh_ctx.weekday = full_time->tm_wday; |
620 | |
621 | return NSERROR_OK; |
622 | } |
623 | |
624 | |
625 | /** |
626 | * Initialise the treeview entries |
627 | * |
628 | * \return NSERROR_OK on success, or appropriate error otherwise |
629 | */ |
630 | static nserror global_history_init_entries(void) |
631 | { |
632 | int i; |
633 | nserror err; |
634 | |
635 | /* Itterate over all global history data, inserting it into treeview */ |
636 | for (i = 0; i < N_DAYS28; i++) { |
637 | struct global_history_entry *l = NULL((void*)0); |
638 | struct global_history_entry *e = gh_list[i]; |
639 | |
640 | /* Insert in reverse order; find last */ |
641 | while (e != NULL((void*)0)) { |
642 | l = e; |
643 | e = e->next; |
644 | } |
645 | |
646 | /* Insert the entries into the treeview */ |
647 | while (l != NULL((void*)0)) { |
648 | err = global_history_entry_insert(l, i); |
649 | if (err != NSERROR_OK) { |
650 | return err; |
651 | } |
652 | l = l->prev; |
653 | } |
654 | } |
655 | |
656 | return NSERROR_OK; |
657 | } |
658 | |
659 | |
660 | static nserror global_history_tree_node_folder_cb( |
661 | struct treeview_node_msg msg, void *data) |
662 | { |
663 | struct global_history_folder *f = data; |
664 | |
665 | switch (msg.msg) { |
666 | case TREE_MSG_NODE_DELETE: |
667 | f->folder = NULL((void*)0); |
668 | break; |
669 | |
670 | case TREE_MSG_NODE_EDIT: |
671 | break; |
672 | |
673 | case TREE_MSG_NODE_LAUNCH: |
674 | break; |
675 | } |
676 | |
677 | return NSERROR_OK; |
678 | } |
679 | |
680 | static nserror |
681 | global_history_tree_node_entry_cb(struct treeview_node_msg msg, void *data) |
682 | { |
683 | struct global_history_entry *e = data; |
684 | nserror ret = NSERROR_OK; |
685 | |
686 | switch (msg.msg) { |
687 | case TREE_MSG_NODE_DELETE: |
688 | e->entry = NULL((void*)0); |
689 | e->user_delete = msg.data.delete.user; |
690 | global_history_delete_entry_internal(e); |
691 | break; |
692 | |
693 | case TREE_MSG_NODE_EDIT: |
694 | break; |
695 | |
696 | case TREE_MSG_NODE_LAUNCH: |
697 | { |
698 | struct browser_window *existing = NULL((void*)0); |
699 | enum browser_window_create_flags flags = BW_CREATE_HISTORY; |
700 | |
701 | /* TODO: Set existing to window that new tab appears in */ |
702 | |
703 | if (msg.data.node_launch.mouse & |
704 | (BROWSER_MOUSE_MOD_1 | BROWSER_MOUSE_MOD_2) || |
705 | existing == NULL((void*)0)) { |
706 | /* Shift or Ctrl launch, open in new window rather |
707 | * than tab. */ |
708 | /* TODO: flags ^= BW_CREATE_TAB; */ |
709 | } |
710 | |
711 | ret = browser_window_create(flags, e->url, NULL((void*)0), |
712 | existing, NULL((void*)0)); |
713 | } |
714 | break; |
715 | } |
716 | return ret; |
717 | } |
718 | |
719 | struct treeview_callback_table gh_tree_cb_t = { |
720 | .folder = global_history_tree_node_folder_cb, |
721 | .entry = global_history_tree_node_entry_cb |
722 | }; |
723 | |
724 | |
725 | /* Exported interface, documented in global_history.h */ |
726 | nserror global_history_init(void *core_window_handle) |
727 | { |
728 | nserror err; |
729 | |
730 | err = treeview_init(); |
731 | if (err != NSERROR_OK) { |
732 | return err; |
733 | } |
734 | |
735 | NSLOG(netsurf, INFO, "Loading global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 735 , }; nslog__log(&_nslog_ctx, "Loading global history"); } } while(0); |
736 | |
737 | /* Init. global history treeview time */ |
738 | err = global_history_initialise_time(); |
739 | if (err != NSERROR_OK) { |
740 | gh_ctx.tree = NULL((void*)0); |
741 | return err; |
742 | } |
743 | |
744 | /* Init. global history treeview entry fields */ |
745 | err = global_history_initialise_entry_fields(); |
746 | if (err != NSERROR_OK) { |
747 | gh_ctx.tree = NULL((void*)0); |
748 | return err; |
749 | } |
750 | |
751 | /* Load the entries */ |
752 | urldb_iterate_entries(global_history_add_entry); |
753 | |
754 | /* Create the global history treeview */ |
755 | err = treeview_create(&gh_ctx.tree, &gh_tree_cb_t, |
756 | N_FIELDS, gh_ctx.fields, |
757 | core_window_handle, |
758 | TREEVIEW_NO_MOVES | TREEVIEW_DEL_EMPTY_DIRS | |
759 | TREEVIEW_SEARCHABLE); |
760 | if (err != NSERROR_OK) { |
761 | gh_ctx.tree = NULL((void*)0); |
762 | return err; |
763 | } |
764 | |
765 | /* Ensure there is a folder for today */ |
766 | err = global_history_create_dir(GH_TODAY); |
767 | if (err != NSERROR_OK) { |
768 | return err; |
769 | } |
770 | |
771 | /* Add the history to the treeview */ |
772 | err = global_history_init_entries(); |
773 | if (err != NSERROR_OK) { |
774 | return err; |
775 | } |
776 | |
777 | /* Expand the "Today" folder node */ |
778 | err = treeview_node_expand(gh_ctx.tree, |
779 | gh_ctx.folders[GH_TODAY].folder); |
780 | if (err != NSERROR_OK) { |
781 | return err; |
782 | } |
783 | |
784 | /* History tree is built |
785 | * We suppress the treeview height callback on entry insertion before |
786 | * the treeview is built. */ |
787 | gh_ctx.built = true1; |
788 | |
789 | /* Inform client of window height */ |
790 | treeview_get_height(gh_ctx.tree); |
791 | |
792 | NSLOG(netsurf, INFO, "Loaded global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 792 , }; nslog__log(&_nslog_ctx, "Loaded global history"); } } while(0); |
793 | |
794 | return NSERROR_OK; |
795 | } |
796 | |
797 | |
798 | /* Exported interface, documented in global_history.h */ |
799 | nserror global_history_fini(void) |
800 | { |
801 | int i; |
802 | nserror err; |
803 | |
804 | NSLOG(netsurf, INFO, "Finalising global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 804 , }; nslog__log(&_nslog_ctx, "Finalising global history") ; } } while(0); |
805 | |
806 | gh_ctx.built = false0; |
807 | |
808 | /* Destroy the global history treeview */ |
809 | err = treeview_destroy(gh_ctx.tree); |
Value stored to 'err' is never read | |
810 | gh_ctx.tree = NULL((void*)0); |
811 | |
812 | /* Free global history treeview entry fields */ |
813 | for (i = 0; i < N_FIELDS; i++) |
814 | if (gh_ctx.fields[i].field != NULL((void*)0)) |
815 | lwc_string_unref(gh_ctx.fields[i].field){ lwc_string *__lwc_s = (gh_ctx.fields[i].field); ((__lwc_s != ((void*)0)) ? (void) (0) : __assert_fail ("__lwc_s != NULL", "desktop/global_history.c", 815, __extension__ __PRETTY_FUNCTION__ )); __lwc_s->refcnt--; if ((__lwc_s->refcnt == 0) || (( __lwc_s->refcnt == 1) && (__lwc_s->insensitive == __lwc_s))) lwc_string_destroy(__lwc_s); }; |
816 | |
817 | err = treeview_fini(); |
818 | if (err != NSERROR_OK) { |
819 | return err; |
820 | } |
821 | |
822 | NSLOG(netsurf, INFO, "Finalised global history")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 822 , }; nslog__log(&_nslog_ctx, "Finalised global history"); } } while(0); |
823 | |
824 | return err; |
825 | } |
826 | |
827 | |
828 | /* Exported interface, documented in global_history.h */ |
829 | nserror global_history_add(nsurl *url) |
830 | { |
831 | const struct url_data *data; |
832 | |
833 | /* If we don't have a global history at the moment, just return OK */ |
834 | if (gh_ctx.tree == NULL((void*)0)) |
835 | return NSERROR_OK; |
836 | |
837 | data = urldb_get_url_data(url); |
838 | if (data == NULL((void*)0)) { |
839 | NSLOG(netsurf, INFO,do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 840 , }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb." ); } } while(0) |
840 | "Can't add URL to history that's not present in urldb.")do { if (NSLOG_LEVEL_INFO >= NSLOG_LEVEL_VERBOSE) { static nslog_entry_context_t _nslog_ctx = { &__nslog_category_netsurf , NSLOG_LEVEL_INFO, "desktop/global_history.c", sizeof("desktop/global_history.c" ) - 1, __PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 1, 840 , }; nslog__log(&_nslog_ctx, "Can't add URL to history that's not present in urldb." ); } } while(0); |
841 | return NSERROR_BAD_PARAMETER; |
842 | } |
843 | |
844 | global_history_add_entry(url, data); |
845 | |
846 | return NSERROR_OK; |
847 | } |
848 | |
849 | |
850 | struct treeview_export_walk_ctx { |
851 | FILE *fp; |
852 | }; |
853 | /** Callback for treeview_walk node entering */ |
854 | static nserror global_history_export_enter_cb(void *ctx, void *node_data, |
855 | enum treeview_node_type type, bool_Bool *abort) |
856 | { |
857 | struct treeview_export_walk_ctx *tw = ctx; |
858 | nserror ret; |
859 | |
860 | if (type == TREE_NODE_ENTRY) { |
861 | struct global_history_entry *e = node_data; |
862 | char *t_text; |
863 | char *u_text; |
864 | |
865 | ret = utf8_to_html(e->data[GH_TITLE].value, "iso-8859-1", |
866 | e->data[GH_TITLE].value_len, &t_text); |
867 | if (ret != NSERROR_OK) |
868 | return NSERROR_SAVE_FAILED; |
869 | |
870 | ret = utf8_to_html(e->data[GH_URL].value, "iso-8859-1", |
871 | e->data[GH_URL].value_len, &u_text); |
872 | if (ret != NSERROR_OK) { |
873 | free(t_text); |
874 | return NSERROR_SAVE_FAILED; |
875 | } |
876 | |
877 | fprintf(tw->fp, "<li><a href=\"%s\">%s</a></li>\n", |
878 | u_text, t_text); |
879 | |
880 | free(t_text); |
881 | free(u_text); |
882 | |
883 | } else if (type == TREE_NODE_FOLDER) { |
884 | struct global_history_folder *f = node_data; |
885 | char *f_text; |
886 | |
887 | ret = utf8_to_html(f->data.value, "iso-8859-1", |
888 | f->data.value_len, &f_text); |
889 | if (ret != NSERROR_OK) |
890 | return NSERROR_SAVE_FAILED; |
891 | |
892 | fprintf(tw->fp, "<li><h4>%s</h4>\n<ul>\n", f_text); |
893 | |
894 | free(f_text); |
895 | } |
896 | |
897 | return NSERROR_OK; |
898 | } |
899 | /** Callback for treeview_walk node leaving */ |
900 | static nserror global_history_export_leave_cb(void *ctx, void *node_data, |
901 | enum treeview_node_type type, bool_Bool *abort) |
902 | { |
903 | struct treeview_export_walk_ctx *tw = ctx; |
904 | |
905 | if (type == TREE_NODE_FOLDER) { |
906 | fputs("</ul></li>\n", tw->fp); |
907 | } |
908 | |
909 | return NSERROR_OK; |
910 | } |
911 | /* Exported interface, documented in global_history.h */ |
912 | nserror global_history_export(const char *path, const char *title) |
913 | { |
914 | struct treeview_export_walk_ctx tw; |
915 | nserror err; |
916 | FILE *fp; |
917 | |
918 | fp = fopen(path, "w"); |
919 | if (fp == NULL((void*)0)) |
920 | return NSERROR_SAVE_FAILED; |
921 | |
922 | if (title == NULL((void*)0)) |
923 | title = "NetSurf Browsing History"; |
924 | |
925 | fputs("<!DOCTYPE html " |
926 | "PUBLIC \"//W3C/DTD HTML 4.01//EN\" " |
927 | "\"http://www.w3.org/TR/html4/strict.dtd\">\n", fp); |
928 | fputs("<html>\n<head>\n", fp); |
929 | fputs("<meta http-equiv=\"Content-Type\" " |
930 | "content=\"text/html; charset=iso-8859-1\">\n", fp); |
931 | fprintf(fp, "<title>%s</title>\n", title); |
932 | fputs("</head>\n<body>\n<ul>\n", fp); |
933 | |
934 | tw.fp = fp; |
935 | err = treeview_walk(gh_ctx.tree, NULL((void*)0), |
936 | global_history_export_enter_cb, |
937 | global_history_export_leave_cb, |
938 | &tw, TREE_NODE_ENTRY | TREE_NODE_FOLDER); |
939 | if (err != NSERROR_OK) |
940 | return err; |
941 | |
942 | fputs("</ul>\n</body>\n</html>\n", fp); |
943 | |
944 | fclose(fp); |
945 | |
946 | return NSERROR_OK; |
947 | } |
948 | |
949 | |
950 | /* Exported interface, documented in global_history.h */ |
951 | void global_history_redraw(int x, int y, struct rect *clip, |
952 | const struct redraw_context *ctx) |
953 | { |
954 | treeview_redraw(gh_ctx.tree, x, y, clip, ctx); |
955 | } |
956 | |
957 | |
958 | /* Exported interface, documented in global_history.h */ |
959 | void global_history_mouse_action(browser_mouse_state mouse, int x, int y) |
960 | { |
961 | treeview_mouse_action(gh_ctx.tree, mouse, x, y); |
962 | } |
963 | |
964 | |
965 | /* Exported interface, documented in global_history.h */ |
966 | bool_Bool global_history_keypress(uint32_t key) |
967 | { |
968 | return treeview_keypress(gh_ctx.tree, key); |
969 | } |
970 | |
971 | |
972 | /* Exported interface, documented in global_history.h */ |
973 | bool_Bool global_history_has_selection(void) |
974 | { |
975 | return treeview_has_selection(gh_ctx.tree); |
976 | } |
977 | |
978 | |
979 | /* Exported interface, documented in global_history.h */ |
980 | bool_Bool global_history_get_selection(nsurl **url, const char **title) |
981 | { |
982 | struct global_history_entry *e; |
983 | enum treeview_node_type type; |
984 | void *v; |
985 | |
986 | type = treeview_get_selection(gh_ctx.tree, &v); |
987 | if (type != TREE_NODE_ENTRY || v == NULL((void*)0)) { |
988 | *url = NULL((void*)0); |
989 | *title = NULL((void*)0); |
990 | return false0; |
991 | } |
992 | |
993 | e = (struct global_history_entry *)v; |
994 | |
995 | *url = e->url; |
996 | *title = e->data[GH_TITLE].value; |
997 | return true1; |
998 | } |
999 | |
1000 | |
1001 | /* Exported interface, documented in global_history.h */ |
1002 | nserror global_history_expand(bool_Bool only_folders) |
1003 | { |
1004 | return treeview_expand(gh_ctx.tree, only_folders); |
1005 | } |
1006 | |
1007 | |
1008 | /* Exported interface, documented in global_history.h */ |
1009 | nserror global_history_contract(bool_Bool all) |
1010 | { |
1011 | return treeview_contract(gh_ctx.tree, all); |
1012 | } |
1013 |