On Tue, 2009-06-23 at 16:14 +0100, John-Mark Bell wrote:
> Index: desktop/history_global_core.c
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ desktop/history_global_core.c 2009-06-23 16:08:42.000000000 +0100
> @@ -0,0 +1,379 @@
> +/*
> + *
Who wrote this? (General point: make sure all the licensing details are
correct in all files you add/change)
> +#include <stdlib.h>
> +
> +#include "content/urldb.h"
> +#include "desktop/browser.h"
> +#include "desktop/history_global_core.h"
> +#include "desktop/options.h"
> +#include "desktop/plotters.h"
> +#include "desktop/tree.h"
> +#include "utils/messages.h"
> +#include "utils/utils.h"
> +#include "utils/log.h"
> +
> +#define MAXIMUM_BASE_NODES 16
> +#define GLOBAL_HISTORY_RECENT_URLS 16
> +
> +static struct node *global_history_base_node[MAXIMUM_BASE_NODES];
> +static int global_history_base_node_time[MAXIMUM_BASE_NODES];
> +static int global_history_base_node_count = 0;
> +
> +static char *global_history_recent_url[GLOBAL_HISTORY_RECENT_URLS];
> +static int global_history_recent_count = 0;
> +
> +static bool global_history_init;
> +
> +struct tree *global_history_tree;
> +
> +
> +static void history_global_initialise_nodes(void);
> +static void history_global_initialise_node(const char *title,
> + time_t base, int days_back);
> +static void history_global_save(void);
> +static bool global_history_add_internal(const char *url,
> + const struct url_data *data);
> +static struct node *history_global_find(const char *url);
> +
> +
> +bool history_global_initialise(uintptr_t handle,
> + void (*start_redraw)(uintptr_t data),
> + void (*end_redraw)(uintptr_t data))
Needs documentation. As does much else; I'll not repeat this everywhere.
> +{
> +
> + char s[MAXIMUM_URL_LENGTH];
> + FILE *fp;
> +
> + /* Create an empty tree */
> + global_history_tree = calloc(sizeof(struct tree), 1);
> + if (!global_history_tree) {
> + warn_user("NoMemory", 0);
> + return false;
> + }
> + global_history_tree->root = tree_create_folder_node(NULL, "Root");
> + if (!global_history_tree->root) {
> + warn_user("NoMemory", 0);
> + free(global_history_tree);
> + global_history_tree = NULL;
> + return false;
> + }
> + global_history_tree->root->expanded = true;
> + history_global_initialise_nodes();
> + global_history_tree->movable = false;
> + global_history_tree->redraw = false;
> + global_history_tree->handle = handle;
> + global_history_tree->start_redraw = start_redraw;
> + global_history_tree->end_redraw = end_redraw;
> +
> + /* load recent URLs */
> + fp = fopen(option_recent_file, "r");
You'll need to ensure that this option is set and non-empty.
> +/**
> + * Internal routine to actually perform global history addition
> + *
> + * \param url The URL to add
> + * \param data URL data associated with URL
> + * \return true (for urldb_iterate_entries)
> + */
> +bool global_history_add_internal(const char *url,
> + const struct url_data *data)
> +{
> + int i, j;
> + struct node *parent = NULL;
> + struct node *link;
> + struct node *node;
> + bool before = false;
> + int visit_date;
> +
> + assert(url && data);
> +
> + visit_date = data->last_visit;
> +
> + /* find parent node */
> + for (i = 0; i < global_history_base_node_count; i++) {
> + if (global_history_base_node_time[i] <= visit_date) {
> + parent = global_history_base_node[i];
> + break;
> + }
> + }
> +
> + /* the entry is too old to care about */
> + if (!parent)
> + return true;
> +
> + if (parent->deleted) {
> + /* parent was deleted, so find place to insert it */
> + link = global_history_tree->root;
> +
> + for (j = global_history_base_node_count - 1; j >= 0; j--) {
> + if (!global_history_base_node[j]->deleted &&
> + global_history_base_node_time[j] >
> + global_history_base_node_time[i]) {
> + link = global_history_base_node[j];
> + before = true;
> + break;
> + }
> + }
> +
> + tree_set_node_selected(global_history_tree,
> + parent, false);
> + tree_set_node_expanded(global_history_tree,
> + parent, false);
> + tree_link_node(link, parent, before);
> +
> + if (!global_history_init) {
> + tree_recalculate_node(global_history_tree, parent,
> true);
> + tree_recalculate_node_positions(global_history_tree,
> + global_history_tree->root);
> + tree_draw(global_history_tree, 0, 0, 16384, 16384);
> + }
> + }
> +
> + /* find any previous occurance */
> + if (!global_history_init) {
> + node = history_global_find(url);
> + if (node) {
> + /* \todo: calculate old/new positions and redraw
> + * only the relevant portion */
> + tree_draw(global_history_tree, 0, 0, 16384, 16384);
What are these magic numbers? Redraw clip box?
> + tree_update_URL_node(node, url, data);
> + tree_delink_node(node);
> + tree_link_node(parent, node, false);
> + tree_handle_node_changed(global_history_tree,
> + node, false, true);
> +/* ro_gui_tree_scroll_visible(hotlist_tree,
> + &node->data);
> +*/ return true;
> + }
> + }
> +
> + /* Add the node at the bottom */
> + node = tree_create_URL_node_shared(parent, url, data);
> + if ((!global_history_init) && (node)) {
> + tree_draw(global_history_tree, node->box.x - NODE_INSTEP, 0,
> + NODE_INSTEP, 16384);
> + tree_handle_node_changed(global_history_tree, node,
> + true, false);
> + }
> +
> + return true;
> +}
> +
> +
> +
> +/**
> + * Adds an URL to the recently used list
> + *
> + * \param url the URL to add (copied)
> + */
> +void global_history_add_recent(const char *url)
> +{
> + int i;
> + int j = -1;
> + char *current;
> +
> + /* try to find a string already there */
> + for (i = 0; i < global_history_recent_count; i++)
> + if (global_history_recent_url[i] &&
> + !strcmp(global_history_recent_url[i], url))
> + j = i;
> +
> + /* already at head of list */
> + if (j == 0)
> + return;
> +
> + if (j < 0) {
> + /* add to head of list */
> + free(global_history_recent_url[
> + GLOBAL_HISTORY_RECENT_URLS - 1]);
> + memmove(&global_history_recent_url[1],
> + &global_history_recent_url[0],
> + (GLOBAL_HISTORY_RECENT_URLS - 1) *
> + sizeof(char *));
> + global_history_recent_url[0] = strdup(url);
> + global_history_recent_count++;
> + if (global_history_recent_count > GLOBAL_HISTORY_RECENT_URLS)
> + global_history_recent_count =
> + GLOBAL_HISTORY_RECENT_URLS;
> + /* TODO: This would be best taken care of in the front end specific
> code as it
> + seems to be necessary on risc os only*/
> + /*if (global_history_recent_count == 1)
> + ro_gui_window_prepare_navigate_all();*/
What is this for?
> Index: gtk/gtk_scaffolding.c
> ===================================================================
> --- gtk/gtk_scaffolding.c (revision 7935)
> +++ gtk/gtk_scaffolding.c (working copy)
> MENUHANDLER(global_history)
> {
> - gtk_widget_show(GTK_WIDGET(wndHistory));
> - gdk_window_raise(GTK_WIDGET(wndHistory)->window);
> + //gtk_widget_show(GTK_WIDGET(wndHistory));
> + //gdk_window_raise(GTK_WIDGET(wndHistory)->window);
Are these redundant? If so, lose them.
> +
> + struct gtk_scaffolding *gw = (struct gtk_scaffolding *) g;
>
> + gtk_window_set_default_size(global_history_window.window, 500, 400);
> + gtk_window_set_position(global_history_window.window,
> GTK_WIN_POS_MOUSE);
> + gtk_window_set_transient_for(global_history_window.window, gw->window);
> + gtk_window_set_opacity(global_history_window.window, 0.9);
> + gtk_widget_show(GTK_WIDGET(global_history_window.window));
> + gdk_window_raise(GTK_WIDGET(global_history_window.window)->window);
> +
> return TRUE;
> }
> Index: gtk/gtk_scaffolding.h
> ===================================================================
> --- gtk/gtk_scaffolding.h (revision 7935)
> +++ gtk/gtk_scaffolding.h (working copy)
> @@ -26,6 +26,13 @@
>
> typedef struct gtk_scaffolding nsgtk_scaffolding;
>
> +struct gtk_history_window {
> + struct gtk_scaffolding *g;
> + GtkWindow *window;
> + GtkScrolledWindow *scrolled;
> + GtkDrawingArea *drawing_area;
> +};
> +
Does this belong here?
> struct gtk_scaffolding {
> GtkWindow *window;
> GtkNotebook *notebook;
> Index: gtk/gtk_treeview.c
> ===================================================================
> --- gtk/gtk_treeview.c (revision 7935)
> +++ gtk/gtk_treeview.c (working copy)
> -void tree_draw_node_expansion(struct tree *tree, struct node *node) {
> +void tree_icon_name_from_filetype(char *buffer, content_type type)
>From the content type, no?
> Index: gtk/gtk_gui.c
> ===================================================================
> --- gtk/gtk_gui.c (revision 7935)
> +++ gtk/gtk_gui.c (working copy)
> @@ -294,6 +294,12 @@
> option_downloads_directory = home;
> }
>
> + if (!option_recent_file) {
> + find_resource(buf, "Recent", "~/.netsurf/Recent");
> + LOG(("Using '%s' as Recent file", buf));
> + option_recent_file = strdup(buf);
You need to check for this failing, and die() appropriately.
> + }
> +
> find_resource(buf, "messages", "./gtk/res/messages");
> LOG(("Using '%s' as Messages file", buf));
> messages_load(buf);
>
> Index: desktop/browser.h
> ===================================================================
> --- desktop/browser.h (revision 7935)
> +++ desktop/browser.h (working copy)
> @@ -191,21 +191,24 @@
> * a drag. */
> BROWSER_MOUSE_CLICK_1 = 4, /* button 1 clicked. */
> BROWSER_MOUSE_CLICK_2 = 8, /* button 2 clicked. */
> + BROWSER_MOUSE_DOUBLE_CLICK = 16, /* button 1 double clicked */
>
> - BROWSER_MOUSE_DRAG_1 = 16, /* start of button 1 drag operation */
> - BROWSER_MOUSE_DRAG_2 = 32, /* start of button 2 drag operation */
> + BROWSER_MOUSE_DRAG_1 = 32, /* start of button 1 drag operation */
> + BROWSER_MOUSE_DRAG_2 = 64, /* start of button 2 drag operation */
>
> - BROWSER_MOUSE_DRAG_ON = 64, /* a drag operation was started and
> + BROWSER_MOUSE_DRAG_ON = 128, /* a drag operation was started and
> * a mouse button is still pressed */
>
> - BROWSER_MOUSE_HOLDING_1 = 128, /* while button 1 drag is in progress */
> - BROWSER_MOUSE_HOLDING_2 = 256, /* while button 2 drag is in progress */
> + BROWSER_MOUSE_HOLDING_1 = 256, /* while button 1 drag is in progress */
> + BROWSER_MOUSE_HOLDING_2 = 512, /* while button 2 drag is in progress */
>
>
> - BROWSER_MOUSE_MOD_1 = 512, /* primary modifier key pressed
> + BROWSER_MOUSE_MOD_1 = 1024, /* primary modifier key pressed
> * (eg. Shift) */
> - BROWSER_MOUSE_MOD_2 = 1024 /* secondary modifier key pressed
> + BROWSER_MOUSE_MOD_2 = 2048, /* secondary modifier key pressed
> * (eg. Ctrl) */
> + BROWSER_MOUSE_MOD_3 = 4096 /* secondary modifier key pressed
> + * (eg. Alt) */
> } browser_mouse_state;
Is this the same as the changes in the textinput branch?
> Index: desktop/tree.c
> ===================================================================
> --- desktop/tree.c (revision 7935)
> +++ desktop/tree.c (working copy)
> static int tree_initialising = 0;
What's this used for?
> * \param expansion the request is the result of a node expansion
> */
> void tree_handle_node_changed(struct tree *tree, struct node *node,
> - bool recalculate_sizes, bool expansion) {
> + bool recalculate_sizes, bool expansion)
> +{
> int width, height;
>
> assert(node);
> @@ -119,10 +147,11 @@
> tree_recalculate_node(tree, node, true);
> if ((node->box.height != height) || (expansion)) {
> tree_recalculate_node_positions(tree, tree->root);
> - tree_redraw_area(tree, 0, node->box.y, 16384, 16384);
> + tree_draw(tree, 0, node->box.y, 16384, 16384);
More magic numbers. There are many similar cases, I'll not flag them
all.
> +/**
> + * Sets a node element as having a specific bitmap.
> + *
> + * \param node the node to update
> + * \param bitmap the bitmap to use
> + */
> +void tree_set_node_bitmap(struct node *node, struct content *bitmap)
> +{
> +
> + assert(node);
> + assert(bitmap);
> +
> + // this is done in tree_callback at the moment
> + /*node->data.bitmap = bitmap;
> + if (!node->data.bitmap) return;*/
Which callback? Is this function even necessary?
> +/**
> + * Sets the sort funstion for a node
> + *
> + * \param node the node to be inserted
> + * \param sort pointer to the sorting function
> + */
> +void tree_set_node_sort_function(struct node *node,
> + int (*sort) (struct node *, struct node *))
> +{
> + struct node *child;
> +
> + node->sort = sort;
> +
> + /* the node had already some children so they must get sorted */
> + if (node->child) {
> +
> + child = node->child;
> + node->child = NULL;
> +
> + while (child) {
> + tree_sort_insert(node, child);
> + child = child->next;
> + }
> +
> + }
> +}
> +
> +/**
> + * Inserts a node into the correct place according to the parents sort
> function
> + *
> + * \param parent the node whose child node 'node' becomes
> + * \param node the node to be inserted
> + */
> +static void tree_sort_insert(struct node *parent, struct node *node)
> +{
> + struct node *after;
> +
> + assert(node);
> + assert(parent);
> + assert(parent->sort);
> +
> + after = parent->last_child;
> + while (after && parent->sort(node, after) == -1)
> + after = after->previous;
> +
> + if (after) {
> + if (after->next)
> + after->next->previous = node;
> + node->next = after->next;
> + node->previous = after;
> + after->next = node;
> + }
> + else {
> + node->previous = NULL;
> + node->next = parent->child;
> + if (parent->child)
> + parent->child->previous = node;
> + parent->child = node;
> + }
> +
> + if (node->next == NULL)
> + parent->last_child = node;
> +
> + node->parent = parent;
> +
> +}
Hm. What's the use case for being able to sort each folder separately?
> +/**
> + * Handles a mouse action for a tree
> + *
> + * \param mouse the mouse state
> + * \param tree the tree to handle a click for
> + * \return whether the click was handled
> + */
> +bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse, int x,
> + int y)
> +{
> + bool furniture;
> + struct node *node;
> + struct node *last;
> + struct node_element *element;
> +
> + assert(tree);
> + assert(tree->root);
> +
> + if (!tree->root->child)
> + return true;
> +
> + tree_initialise_redraw(tree);
> + element = tree_get_node_element_at(tree->root->child, x, y, &furniture);
> +
> + /* stop editing for anything but a drag */
> +// if ((tree->editing) && /*(pointer->i != tree->edit_handle) &&*/
> +// !(mouse & BROWSER_MOUSE_DRAG_1))
> +// ro_gui_tree_stop_edit(tree);
> +
> + /* handle a menu click */
> +// if (pointer->buttons == wimp_CLICK_MENU) {
> +// if ((!element) || (!tree->root->child) ||
> +// (tree_has_selection(tree->root->child)))
> +// return true;
> +//
> +// node = element->parent;
> +// tree->temp_selection = node;
> +// node->selected = true;
> +// tree_handle_node_element_changed(tree, &node->data);
> +// return true;
> +//
> +// }
If this is redundant, it should go.
> +
> +// node = tree_get_selected_node(tree->root);
> +// if (node) {
> +// if (node->folder) {
> +// if ((node->expanded) &&
> +//
> (ro_gui_wimp_sprite_exists("directoryo")))
> +// sprintf(ro_gui_tree_drag_name,
> "directoryo");
> +// else
> +// sprintf(ro_gui_tree_drag_name,
> "directory");
> +// } else {
> +// /* small_xxx -> file_xxx */
> +// sprintf(ro_gui_tree_drag_name, "file_%s",
> +// node->data.sprite->name + 6);
> +// if
> (!ro_gui_wimp_sprite_exists(ro_gui_tree_drag_name))
> +// sprintf(ro_gui_tree_drag_name,
> "file_xxx");
> +// }
> +// } else {
> +// sprintf(ro_gui_tree_drag_name, "package");
> +// }
> +//
> +// error = xdragasprite_start(dragasprite_HPOS_CENTRE |
> +// dragasprite_VPOS_CENTRE |
> +// dragasprite_BOUND_POINTER |
> +// dragasprite_DROP_SHADOW,
> +// (osspriteop_area *) 1,
> +// ro_gui_tree_drag_name, &box, 0);
> +// if (error)
> +// LOG(("xdragasprite_start: 0x%x: %s",
> +// error->errnum, error->errmess));
> + return true;
Similarly.
> Index: desktop/tree.h
> ===================================================================
> --- desktop/tree.h (revision 7935)
> +++ desktop/tree.h (working copy)
> @@ -26,10 +26,19 @@
> #include <stdbool.h>
> #include <stdint.h>
>
> +#include "desktop/browser.h"
> +#include "image/bitmap.h"
> +
> struct url_data;
> struct cookie_data;
>
> typedef enum {
> + TREE_NO_DRAG = 0,
> + TREE_SELECT_DRAG,
> + TREE_MOVE_DRAG
> +} tree_drag_type;
> +
> +typedef enum {
> TREE_ELEMENT_URL,
> TREE_ELEMENT_ADDED,
> TREE_ELEMENT_LAST_VISIT,
> @@ -50,14 +59,22 @@
> TREE_ELEMENT_SSL
> } node_element_data;
Do we really need all these node data types? I'd prefer to see
primitives and then client-defined event handlers.
So, basically; text, text + bitmap, bitmap.
> -#define NODE_INSTEP 40
> +typedef void(*tree_start_radraw_callback)(uintptr_t data);
> +typedef void(*tree_end_radraw_callback)(uintptr_t data);
s/radraw/redraw/
> -struct node_sprite;
> +#define NODE_INSTEP 20
> +#define TREE_TEXT_HEIGHT 20
> +#define THUMBNAIL_WIDTH 100
> +#define THUMBNAIL_HEIGHT 86
> +#define MAXIMUM_URL_LENGTH 1024
These are implementation details and should be hidden, no?
In fact, the URL_LENGTH and thumbnail dimensions probably shouldn't
exist at all.
> struct toolbar;
> +struct node;
> +struct tree;
>
> typedef enum {
> NODE_ELEMENT_TEXT, /* <-- Text only */
> - NODE_ELEMENT_TEXT_PLUS_SPRITE, /* <-- Text and sprite */
> + NODE_ELEMENT_TEXT_PLUS_BITMAP, /* <-- Text and bitmap */
> NODE_ELEMENT_THUMBNAIL, /* <-- Bitmap only */
Rename to NODE_ELEMENT_BITMAP.
> } node_element_type;
Huzzah. These are the primitives I wanted, above. Why should the tree
care about what semantics the data contained in a given node have? It
should simply be interested in the element type.
> @@ -75,7 +92,7 @@
> node_element_type type; /* <-- Element type */
> struct node_element_box box; /* <-- Element bounding box */
> const char *text; /* <-- Text for the element */
> - struct node_sprite *sprite; /* <-- Sprite for the element */
> + struct content *bitmap; /* <-- Bitmap for the element */
> struct node_element *next; /* <-- Next node element */
> node_element_data data; /* <-- Data being represented */
> };
> @@ -96,11 +113,12 @@
> struct node *last_child; /* <-- Last child */
> struct node *previous; /* <-- Previous child of the parent */
> struct node *next; /* <-- Next child of the parent */
> -
> + int (*sort) (struct node *, struct node *); /* <-- Sorting function
> for the node (for folder nodes only)*/
> + void (*action) (struct tree *tree, struct node *node);
> };
>
> struct tree {
> - unsigned int handle; /* <-- User assigned handle */
> + uintptr_t handle; /* <-- User assigned handle */
> int offset_x; /* <-- User assigned tree x offset */
> int offset_y; /* <-- User assigned tree y offset */
> struct node *root; /* <-- Tree root element */
> @@ -118,24 +136,25 @@
> struct node_element *editing; /* <-- Node element being edited */
> struct node *temp_selection; /* <-- Temporarily selected node */
> struct toolbar *toolbar; /* <-- Tree toolbar */
> + bool redraw; /* <-- Flag indicating whether the tree
> should be redrawn on layout changes*/
> + tree_drag_type drag;
> + tree_start_radraw_callback start_redraw;
> + tree_end_radraw_callback end_redraw;
> };
>
> +/* callback update */
> +struct node_update {
> + struct tree *tree;
> + struct node *node;
> +};
Please hide all the above implementation details within tree.c and then
provide accessors/mutators for those items of data that clients require
access to.
> /* Non-platform specific code */
> void tree_initialise(struct tree *tree);
> -void tree_initialise_nodes(struct tree *tree, struct node *root);
> void tree_handle_node_changed(struct tree *tree, struct node *node,
> bool recalculate_sizes, bool expansion);
> -void tree_handle_node_element_changed(struct tree *tree,
> - struct node_element *element);
> void tree_recalculate_node(struct tree *tree, struct node *node, bool
> recalculate_sizes);
> void tree_recalculate_node_positions(struct tree *tree, struct node *root);
> -struct node *tree_get_node_at(struct node *root, int x, int y, bool
> *furniture);
> -struct node_element *tree_get_node_element_at(struct node *node, int x, int
> y,
> - bool *furniture);
> struct node_element *tree_find_element(struct node *node, node_element_data
> data);
> -void tree_move_selected_nodes(struct tree *tree, struct node *destination,
> - bool before);
> bool tree_has_selection(struct node *node);
> void tree_draw(struct tree *tree, int clip_x, int clip_y, int clip_width,
> int clip_height);
> @@ -150,33 +169,32 @@
> const char *url, const struct url_data *data);
Can this API be rationalised? It looks pretty complicated. (This also
applies to the rest of this API, below)
> struct node *tree_create_cookie_node(struct node *parent,
> const struct cookie_data *data);
This isn't something the generic tree code should care about. It's down
to the specific client to do this.
> -void tree_set_node_sprite(struct node *node, const char *sprite,
> - const char *expanded);
> +void tree_set_node_bitmap(struct node *node, struct content *bitmap);
> void tree_set_node_expanded(struct tree *tree, struct node *node, bool
> expanded);
> void tree_set_node_selected(struct tree *tree, struct node *node,
> bool selected);
> -void tree_handle_selection_area(struct tree *tree, int x, int y, int width,
> - int height, bool invert);
> void tree_delete_selected_nodes(struct tree *tree, struct node *node);
> void tree_delete_node(struct tree *tree, struct node *node, bool siblings);
> -void tree_recalculate_size(struct tree *tree);
> bool tree_handle_expansion(struct tree *tree, struct node *node, bool
> expanded,
> bool folder, bool leaf);
> struct node *tree_get_selected_node(struct node *node);
> struct node *tree_get_link_details(struct tree *tree, int x, int y,
> bool *before);
> -
> -
> +void tree_update_URL_node(struct node *node, const char *url,
> + const struct url_data *data);
Similarly, not generic and should be removed.
> +void tree_set_node_sort_function(struct node *node,
> + int (*sort) (struct node *, struct node *));
> +int tree_alphabetical_sort(struct node *, struct node *);
> +bool tree_mouse_action(struct tree *tree, browser_mouse_state mouse,
> + int x, int y);
> +void tree_drag_end(struct tree *tree, browser_mouse_state mouse, int x0, int
> y0,
> + int x1, int y1);
> +
> /* Platform specific code */
> void tree_initialise_redraw(struct tree *tree);
> -void tree_redraw_area(struct tree *tree, int x, int y, int width, int
> height);
> -void tree_draw_line(int x, int y, int width, int height);
> -void tree_draw_node_element(struct tree *tree, struct node_element *element);
> -void tree_draw_node_expansion(struct tree *tree, struct node *node);
> -void tree_recalculate_node_element(struct node_element *element);
> -void tree_update_URL_node(struct node *node, const char *url,
> - const struct url_data *data);
> void tree_resized(struct tree *tree);
> -void tree_set_node_sprite_folder(struct node *node);
> -
> +struct content *tree_get_bitmap(struct node *node, const char *bitmap,
> + bool path);
> +void tree_icon_name_from_filetype(char *buffer, content_type type);
> +void tree_scroll_visible(struct tree *tree, struct node_element *element);
> #endif
J.