| 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); if (__lwc_s != ((void*)0)) { __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); | |||
| ||||
| 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); if (__lwc_s != ((void*)0)) { __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 |