Statuses

Custom GtkTreeModel for a file browser

In Bluefish, gtk+, open source, Programming on October 17, 2013 by oli4444

Similar to other IDE’s and editors, Bluefish has a filebrowser in a side pane of the GUI. Previously Bluefish used a GtkTreeStore to store the icon and filename for each file/directory. To improve speed and reduce memory usage, I recently wrote a custom GtkTreeModel to replace the old store. This is the first post on the design of the custom TreeModel. The final code can be found here: https://sourceforge.net/p/bluefish/code/HEAD/tree/trunk/bluefish/src/file_treemodel.c

Bluefish Screenshot from 2013-10-17 14:14:23

I started with the excellent tutorial from Tim-Philipp M├╝ller http://scentric.net/tutorial/

First thing I did was modifying the tutorial code from a listmodel to a treemodel. Therefore each record has a pointer “parent”, and each record should store it’s children.

One of the design decisions was how to store children: as a linked list, or an array of records, or an array of pointers to records. Some thoughts: The linked list is faster if you have to insert an entry somewhere in the middle, or remove one (with an array this requires realloc()). The array is faster if you need the nth element (with a linked list you need to traverse the list). The array also brings another advantage: use qsort for sorting (but the g_list_ functions have a good sort function for lists as well), and bsearch for searching if an entry exists (a linked list requires again to traverse all entries to see if an entry exists). An array of records needs more memory copying during sort, insert or delete. An array of pointers needs more memory (one extra pointer for each record).

In a file tree, most directories have the same files for most of the times. Sometimes a new file is added, or a file is deleted, but most of the time the list is stable. So I chose the array of pointers. But I’m still doubting if I should have chosen an array of records.

The minimal record looks like this:


struct _UriRecord {
gchar *name;
UriRecord *parent;
UriRecord **rows;
guint num_rows;
};

The file browser pane in Bluefish shows icons, and can show a name in bold or normal weight. The compare function for sorting returns directories before files, so I need to know if a record is a directory or a file. Bluefish also has filtering possibilities based on filename or mime type. So these properties are all added to each record.

Because I chose an array of pointers, it is costly to find the next item in the array from a record. Therefore I added the position in the array as property. The next one is simply pos+1. This has another advantage: after sorting, the TreeModel needs to inform all listerers that the order has changed. For that you need the old and the new order. Since the old order is stored in pos this is easily done in a loop over the array.

In the Bluefish code I oten need to convert from a GFile to a position in the TreeModel and vice versa. I therefore added a hash table to the treemodel with the GFile as key and the record as value. Since a GFile is refcounted, It takes only a pointer to add it to the record as well.

At last I need a way to refresh directories when I re-read them. I could simply delete them all, and add them again, but that would also delete the sub-directories. So I added a “possibly_deleted” property that is set to 1 before directory re-read, and set to “0″ if an entry still exists. After closing the directory every record that still has “possibly_deleted” set to 1 can be removed.

To reduce memory usage, I changed several properties to 16bit or 8bit values. This requires an extra shift when accessing the properties, but with many records it reduces memory usage. These are the properties that are only needed when the records change (and directories are usually pretty stable).

The resulting record looks like this:


struct _UriRecord {
gchar *name;
gchar *icon_name;
gchar *fast_content_type;
GFile *uri;
UriRecord *parent;
UriRecord **rows;
guint16 num_rows;
guint16 pos;
guint16 weight;
guint8 isdir;
guint8 possibly_deleted;
};

On 64 bit systems this results in: 6*8bytes + 3*2bytes + 2*1byte = 56 bytes per record.

On 32 bit systems this results in : 6*4bytes + 3*2bytes + 2*1byte = 32 bytes per record.

More about this code in a few days. Any comments how this design could be improved? Please post a comment.

About these ads

One Response to “Custom GtkTreeModel for a file browser”

  1. A much simpler solution is to display at most one directory contents at a time. A GtkListStore is in this case sufficient. With a small toolbar with buttons like “go to the parent directory”, this gives a good result, IMO (that’s what I did in LaTeXila). The side panel is anyway not very large, so it’s not convenient to display a deep hierarchy of directories.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: