We’ve been doing a bunch of work on Ewl_Tree2 lately. It’s been shaping up nicely at the moment. To that end, I thought I’d do a quick write up on how it works and what you can do with it.

Ewl_Tree2 is built based on an MVC (model/view/controller) framework. This makes it a lot easier for developers to keep their data up to date without having to go through all kinds of contortions using the tree nodes and widgets as they do with the current Ewl_Tree widget.

There are three basic items you’ll need to become familiar with in order to use Ewl_Tree2. They are, Ewl_Tree2, of course, Ewl_Model and Ewl_View. These three will allow you to setup your columns and the tree.

The best way to show something is through an example, so that’s what I’ll do. This is basically the tree2 test case from _ewl_test_ ported to run in a window. The case is pretty simple. We store an array of data, this array contains nodes that specify the text and an image to be displayed.

We then create a three column tree. The first column will show the text as an Ewl_Label widget. The second column will display an Ewl_Image. The third will be an Ewl_Button. The third column doesn’t use the ewl_button code directly as we want to set two pieces of information so we write our own methods to handle the creating and assignment functions.

I’ll be putting the code into tree2_test.c and using the following to compile the code as I go.

oni:~/dev/tree2_test$ gcc -o tree2_test tree2_test.c `ewl-config --cflags --libs

With that out of the way, on with the show. I’m going to start by listing all of the code and then I’ll go through it piece by piece.

#include <Ewl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DATA_ELEMENTS 5

typedef struct Test_Row_Data Test_Row_Data;
struct Test_Row_Data
{
    char *image;
    char *text;
};

typedef struct Test_Data Test_Data;
struct Test_Data
{
    unsigned int count;
    Test_Row_Data **rows;
};

static void *test_data_setup(void);

static Ewl_Widget *test_custom_new(void);
static void test_custom_assign_set(Ewl_Widget *w, void *data);

static Ewl_Widget *test_data_header_fetch(void *data, int column);
static void *test_data_fetch(void *data, unsigned int row, unsigned int column);
static void test_data_sort(void *data, unsigned int column, Ewl_Sort_Direction sort);
static int test_data_count_get(void *data);

static void cb_delete_window(Ewl_Widget *w, void *ev, void *data);
static void cb_scroll_headers(Ewl_Widget *w, void *ev, void *data);
static void cb_scroll_visible(Ewl_Widget *w, void *ev, void *data);

int
main(int argc, char ** argv)
{
    Ewl_Widget *tree, *box, *o, *o2;
    Ewl_Model *model;
    Ewl_View *view;
    void *data;

    /* make sure we can setup ewl */
    if (!ewl_init(&argc, argv))
    {
        fprintf(stderr, "Unable to init ewl.\n");
        return 1;
    }

    /* create the window */
    o = ewl_window_new();
    ewl_window_title_set(EWL_WINDOW(o), "tree2 example");
    ewl_window_class_set(EWL_WINDOW(o), "tree2_example");
    ewl_window_name_set(EWL_WINDOW(o), "tree2_example");
    ewl_object_size_request(EWL_OBJECT(o), 640, 480);
    ewl_callback_append(o, EWL_CALLBACK_DELETE_WINDOW, cb_delete_window, NULL);
    ewl_widget_show(o);

    box = ewl_vbox_new();
    ewl_container_child_append(EWL_CONTAINER(o), box);
    ewl_widget_show(box);

    o2 = ewl_hbox_new();
    ewl_container_child_append(EWL_CONTAINER(box), o2);
    ewl_object_fill_policy_set(EWL_OBJECT(o2),
                EWL_FLAG_FILL_VSHRINK | EWL_FLAG_FILL_HFILL);
    ewl_widget_show(o2);

    /* create our data */
    data = test_data_setup();

    /* create the model that'll be used for the first two columns */
    model = ewl_model_new();
    ewl_model_fetch_set(model, test_data_fetch);
    ewl_model_sort_set(model, test_data_sort);
    ewl_model_count_set(model, test_data_count_get);

    tree = ewl_tree2_new();
    ewl_container_child_append(EWL_CONTAINER(box), tree);
    ewl_object_fill_policy_set(EWL_OBJECT(tree), EWL_FLAG_FILL_ALL);
    ewl_tree2_data_set(EWL_TREE2(tree), data);
    ewl_widget_show(tree);

    /* create a view for the first column that just has an ewl label */
    view = ewl_view_new();
    ewl_view_constructor_set(view, ewl_label_new);
    ewl_view_assign_set(view, EWL_VIEW_ASSIGN(ewl_label_text_set));
    ewl_view_header_fetch_set(view, test_data_header_fetch);
    ewl_tree2_column_append(EWL_TREE2(tree), model, view);

    /* create a view for the second column that just has an ewl image */
    view = ewl_view_new();
    ewl_view_constructor_set(view, ewl_image_new);
    ewl_view_assign_set(view, EWL_VIEW_ASSIGN(ewl_image_file_path_set));
    ewl_view_header_fetch_set(view, test_data_header_fetch);
    ewl_tree2_column_append(EWL_TREE2(tree), model, view);

    /* we don't want this one sortable */
    model = ewl_model_new();
    ewl_model_fetch_set(model, test_data_fetch);
    ewl_model_count_set(model, test_data_count_get);

    /* create a view for the third column that has a custom widget */
    view = ewl_view_new();
    ewl_view_constructor_set(view, test_custom_new);
    ewl_view_assign_set(view, test_custom_assign_set);
    ewl_view_header_fetch_set(view, test_data_header_fetch);
    ewl_tree2_column_append(EWL_TREE2(tree), model, view);

    /* create the checkbuttons for the top box */
    o = ewl_checkbutton_new();
    ewl_button_label_set(EWL_BUTTON(o), "Scroll headers");
    ewl_container_child_append(EWL_CONTAINER(o2), o);
    ewl_callback_append(o, EWL_CALLBACK_CLICKED,
                cb_scroll_headers, tree);
    ewl_widget_show(o);

    o = ewl_checkbutton_new();
    ewl_button_label_set(EWL_BUTTON(o), "Scroll visible");
    ewl_container_child_append(EWL_CONTAINER(o2), o);
    ewl_checkbutton_checked_set(EWL_CHECKBUTTON(o), TRUE);
    ewl_callback_append(o, EWL_CALLBACK_CLICKED,
                cb_scroll_visible, tree);
    ewl_widget_show(o);

    ewl_main();
    return 0;
}

/* setup our data */
static void *
test_data_setup(void)
{
    Test_Data *data;
    Test_Row_Data **dt;

    data = calloc(1, sizeof(Test_Data));
    dt = calloc(DATA_ELEMENTS, sizeof(Test_Row_Data *));

    dt[0] = calloc(1, sizeof(Test_Row_Data));
    dt[0]->image = strdup("/usr/local/share/ewl/images/e-logo.png");
    dt[0]->text = strdup("The E logo");

    dt[1] = calloc(1, sizeof(Test_Row_Data));
    dt[1]->image = strdup("/usr/local/share/ewl/images/elicit.png");
    dt[1]->text = strdup("The Elicit image");

    dt[2] = calloc(1, sizeof(Test_Row_Data));
    dt[2]->image = strdup("/usr/local/share/ewl/images/entrance.png");
    dt[2]->text = strdup("The Entrance image");

    dt[3] = calloc(1, sizeof(Test_Row_Data));
    dt[3]->image = strdup("/usr/local/share/ewl/images/End.png");
    dt[3]->text = strdup("Zebra");

    dt[4] = calloc(1, sizeof(Test_Row_Data));
    dt[4]->image = strdup("/usr/local/share/ewl/images/banner-top.png");
    dt[4]->text = strdup("Ant");

    data->rows = dt;
    data->count = DATA_ELEMENTS;

    return data;
}

static Ewl_Widget *
test_custom_new(void)
{
    Ewl_Widget *button;

    button = ewl_button_new();

    return button;
}

static void
test_custom_assign_set(Ewl_Widget *w, void *data)
{
    Test_Row_Data *d;

    d = data;
    ewl_button_label_set(EWL_BUTTON(w), d->text);
    ewl_button_image_set(EWL_BUTTON(w), d->image, NULL);
}

static Ewl_Widget *
test_data_header_fetch(void *data , int column)
{
    Ewl_Widget *l;

    l = ewl_label_new();
    if (column == 0)
        ewl_label_text_set(EWL_LABEL(l), "Title");
    else if (column == 1)
        ewl_label_text_set(EWL_LABEL(l), "Image");
    else
        ewl_label_text_set(EWL_LABEL(l), "Button");
    ewl_widget_show(l);

    return l;
}

static void *
test_data_fetch(void *data, unsigned int row, unsigned int column)
{
    Test_Data *d;
    void *val = NULL;

    d = data;

    if (column == 0)
        val = d->rows[row]->text;

    else if (column == 1)
        val = d->rows[row]->image;

    else if (column == 2)
        val = d->rows[row];

    return val;
}

static void
test_data_sort(void *data, unsigned int column, Ewl_Sort_Direction sort)
{
    Test_Data *d;
    int i;

    /* just leave it if we're in sort none. */
    if (sort == EWL_SORT_DIRECTION_NONE)
        return;

    d = data;

    for (i = (DATA_ELEMENTS - 1); i >= 0; i--)
    {
        int j;

        for (j = 1; j <= i; j++)
        {
            char *a, *b;

            if (column == 0)
            {
                a = d->rows[j - 1]->text;
                b = d->rows[j]->text;
            }
            else
            {
                a = d->rows[j - 1]->image;
                b = d->rows[j]->image;
            }

            if (((sort == EWL_SORT_DIRECTION_ASCENDING) && strcmp(a, b) > 0)
                    || ((sort == EWL_SORT_DIRECTION_DESCENDING)
                        && strcmp(a, b) < 0))
            {
                char *temp;

                temp = d->rows[j - 1]->text;
                d->rows[j - 1]->text = d->rows[j]->text;
                d->rows[j]->text = temp;

                temp = d->rows[j - 1]->image;
                d->rows[j - 1]->image = d->rows[j]->image;
                d->rows[j]->image = temp;
            }
        }
    }
}

static int
test_data_count_get(void *data)
{
    Test_Data *d;

    d = data;

    return d->count;
}

static void
cb_delete_window(Ewl_Widget *w, void *ev, void *data)
{
    ewl_widget_destroy(w);
    ewl_main_quit();
}

static void
cb_scroll_headers(Ewl_Widget *w, void *ev , void *data)
{
    Ewl_Tree2 *tree;

    tree = data;
    ewl_tree2_scroll_headers_set(tree,
            ewl_checkbutton_is_checked(EWL_CHECKBUTTON(w)));
}

static void
cb_scroll_visible(Ewl_Widget *w, void *ev , void *data)
{
    Ewl_Tree2 *tree;

    tree = data;
    ewl_tree2_scroll_visible_set(tree,
            ewl_checkbutton_is_checked(EWL_CHECKBUTTON(w)));
}

Simple enough, eh? Ok, maybe I should go through it then.

#include <Ewl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

We start off with the standard set of includes. Ewl.h is obviously required to do any EWL programming. The others are needed as we’ll be using functions they define throughout the application.

#define DATA_ELEMENTS 5

typedef struct Test_Row_Data Test_Row_Data;
struct Test_Row_Data
{
    char *image;
    char *text;
};

typedef struct Test_Data Test_Data;
struct Test_Data
{
    unsigned int count;
    Test_Row_Data **rows;
};

I’m not planning on doing anything fancy with my data. Just keeping an array with five elements. I’m using a define DATA_ELEMENTS to store the number of elements as I’ll be using this in a few places. The data will be stored in a Test_Data structure. This struct will store the number of items in the array and an array of Test_Row_Data pointers. Test_Row_Data structs just store the text and image strings for each of our rows.

static void *test_data_setup(void);

static Ewl_Widget *test_custom_new(void);
static void test_custom_assign_set(Ewl_Widget *w, void *data);

static Ewl_Widget *test_data_header_fetch(void *data, int column);
static void *test_data_fetch(void *data, unsigned int row, unsigned int column);
static void test_data_sort(void *data, unsigned int column, Ewl_Sort_Direction sort);
static int test_data_count_get(void *data);

static void cb_delete_window(Ewl_Widget *w, void *ev, void *data);
static void cb_scroll_headers(Ewl_Widget *w, void *ev, void *data);
static void cb_scroll_visible(Ewl_Widget *w, void *ev, void *data);

As you can see, a bunch of pre-declarations next. We’ll be seeing, and getting the explanation for these as we go along.

int main(int argc, char ** argv)
{
    Ewl_Widget *tree, *box, *o, *o2;
    Ewl_Model *model;
    Ewl_View *view;
    void *data;

    /* make sure we can setup ewl */
    if (!ewl_init(&argc, argv))
    {
        fprintf(stderr, "Unable to init ewl.\n");
        return 1;
    }

The first step in any EWL application is to initialize EWL itself. This is done with a call to ewl_init(). ewl_init() accepts two parameters, the argc and argv arguments that were passed to your application. ewl_init() will return TRUE if EWL was successfully initialized or FALSE otherwise. The reason to pass the args to ewl_init() is so that EWL can parse out any EWL specific arguments. Things like setting the rendering engine or printing the EWL help documentation. These parameters can both be safely set to NULL if desired.

With EWL setup we can get down the the fun bit of creating the UI.

/* create the window */
o = ewl_window_new();
ewl_window_title_set(EWL_WINDOW(o), "tree2 example");
ewl_window_class_set(EWL_WINDOW(o), "tree2_example");
ewl_window_name_set(EWL_WINDOW(o), "tree2_example");
ewl_object_size_request(EWL_OBJECT(o), 640, 480);
ewl_callback_append(o, EWL_CALLBACK_DELETE_WINDOW, cb_delete_window, NULL);
ewl_widget_show(o);

box = ewl_vbox_new();
ewl_container_child_append(EWL_CONTAINER(o), box);
ewl_widget_show(box);

o2 = ewl_hbox_new();
ewl_container_child_append(EWL_CONTAINER(box), o2);
ewl_object_fill_policy_set(EWL_OBJECT(o2), EWL_FLAG_FILL_VSHRINK | EWL_FLAG_FILL_HFILL);
ewl_widget_show(o2);

This little chunks sets up our base UI. We create the Ewl_Window first using ewl_window_new(). We set the title, class and name for the window and then give it a default size of 640x480. Once this is done we set a callback for when the window is destroyed. This is done with ewl_callback_append() call. We want to get notified when the window receives the EWL_CALLBACK_DELETE_WINDOW callback by having the cb_delete_window() function executed.

With the main window setup we create an Ewl_Box inside of it. An Ewl_Window by default has no layout policy so if you pack several widgets into it they’ll all be sitting on top of each other. You have to pack a box, or something, in there to handle the layout of the contents. You’ll see we use ewl_container_child_append() to add the box to the window. You’ll be seeing this a lot as we pack the UI together. There are also ewl_container_child_prepend() and ewl_container_child_insert() calls that can be used.

We also create a second Ewl_Box to hold some checkbuttons that we’ll create later. We’re creating the box here just because I like to keep things order. It’s the first item in the window so I create and pack it first. We could also use ewl_container_child_prepend() later to add it to the box if we wished. We’re setting a custom fill policy on this box with ewl_object_fill_policy_set(). The policy we’re setting is EWL_FLAG_FILL_VSHIRNK | EWL_FLAG_FILL_HFILL. We’re telling the widget that we want it to be as small as possible vertically but take up as much space as possible horizontally. The fill policy is just a bit mask so we’re bit wise or’ing the values together.

I guess a quick note about EWL inheritance is necessary at some point. EWL uses an object oriented approach to it’s widgets. All widgets inherit from Ewl_Widget. Ewl_Widget inherits from Ewl_Object. So any ewl_widget_* or ewl_object_* call can be called on any widgets. You just need to cast to the correct type first. The casts are done with the EWL_WIDGET() or EWL_OBJECT() macros. All widgets in EWL have an EWL_WIDGET_NAME() macro. There’s a lot of inheritance going on in EWL. The Ewl_Box code inherits from the Ewl_Container code. So any ewl_container_* call will work on an Ewl_Box, you just have to wrap the box variable with an EWL_CONTAINER() call. Take a look at the EWL docs for a complete listing of the inheritance. (Or if you’re looking at the header files the inheritance is always the first item in the struct.)

/* create our data */
data = test_data_setup();

This is just a convenience function to create our data array. I like to keep stuff separated out whenever possible. We’ll see what this does later.

/* create the model that'll be used for the first two columns */
model = ewl_model_new();
ewl_model_fetch_set(model, test_data_fetch);
ewl_model_sort_set(model, test_data_sort);
ewl_model_count_set(model, test_data_count_get);

Our first two columns in the tree will actually use the same data model. I don’t want the third column to be sortable so we need to use a slightly different model (although it’s almost the same). We need to set at a of minimum two pieces of data into the Ewl_Model. These are, the function to fetch the model data, set with ewl_model_fetch_set(), and the function to get a count of the number of rows of data, set with ewl_model_count_set(). I’m using the third ewl_model_sort_set() to set a sort function for the first two columns. Each of these calls takes the model and a function pointer. You can see the function signature at the top of the file. We’ll take a closer look at these functions a bit later.

tree = ewl_tree2_new();
ewl_container_child_append(EWL_CONTAINER(box), tree);
ewl_object_fill_policy_set(EWL_OBJECT(tree), EWL_FLAG_FILL_ALL);
ewl_tree2_data_set(EWL_TREE2(tree), data);
ewl_widget_show(tree);

Next we create the tree itself. The tree is packed into the box we created earlier. We set the box with a fill policy of EWL_FLAG_FILL_ALL so it will take as much, or as little space as necesary. We then set the data we created into the tree with ewl_tree2_data_set() function. This data will be passed around to our various functions as the tree does its work.

/* create a view for the first column that just has an ewl label */
view = ewl_view_new();
ewl_view_constructor_set(view, ewl_label_new);
ewl_view_assign_set(view, EWL_VIEW_ASSIGN(ewl_label_text_set));
ewl_view_header_fetch_set(view, test_data_header_fetch);
ewl_tree2_column_append(EWL_TREE2(tree), model, view);

Ok, with our tree created we can start to add columns. We’ve already created the Ewl_Model for the first two columns so we just need to create the views.

As I mentioned before the first column is using Ewl_Label to display the text of the data item. So, we create a new Ewl_View with ewl_view_new(). We then set the constructor for this column with ewl_view_constructor_set(). This will be used to create a new widget for the item in the column. Then we use ewl_view_assign_set() to set the function that will be used to assign data into our widget for a given cell. Tree columns need a header. We’ll set a third callback function to get the header data for the tree. We use ewl_view_header_fetch_set() to set this function.

Once our view is created we call ewl_tree2_column_append() to create a new column in the tree using our model and view.

/* create a view for the second column that just has an ewl image */
view = ewl_view_new();
ewl_view_constructor_set(view, ewl_image_new);
ewl_view_assign_set(view, EWL_VIEW_ASSIGN(ewl_image_file_path_set));
ewl_view_header_fetch_set(view, test_data_header_fetch);
ewl_tree2_column_append(EWL_TREE2(tree), model, view);

The second column in the tree is created the same as the first except we use the Ewl_Image functions instead of the Ewl_Label functions. You can use any widget within EWL to setup a view. This gives the tree the flexibility of being able to display any desired widget, even custom designed widgets.

/* we don't want this one sortable */
model = ewl_model_new();
ewl_model_fetch_set(model, test_data_fetch);
ewl_model_count_set(model, test_data_count_get);

/* create a view for the third column that has a custom widget */
view = ewl_view_new();
ewl_view_constructor_set(view, test_custom_new);
ewl_view_assign_set(view, test_custom_assign_set);
ewl_view_header_fetch_set(view, test_data_header_fetch);
ewl_tree2_column_append(EWL_TREE2(tree), model, view);

The third column in the tree is similar to the first two except we don’t want it sortable. So, we create a new Ewl_Model and set the fetch and count functions as before but skip the sort function. We then create the view and use our custom constructor and assignment functions instead of a standard EWL set. With the model and view created we append the column into the tree.

/* create the checkbuttons for the top box */
o = ewl_checkbutton_new();
ewl_button_label_set(EWL_BUTTON(o), "Scroll headers");
ewl_container_child_append(EWL_CONTAINER(o2), o);
ewl_callback_append(o, EWL_CALLBACK_CLICKED, cb_scroll_headers, tree);
ewl_widget_show(o);

o = ewl_checkbutton_new();
ewl_button_label_set(EWL_BUTTON(o), "Scroll visible");
ewl_container_child_append(EWL_CONTAINER(o2), o);
ewl_checkbutton_checked_set(EWL_CHECKBUTTON(o), TRUE);
ewl_callback_append(o, EWL_CALLBACK_CLICKED, cb_scroll_visible, tree);
ewl_widget_show(o);

The final piece of the UI to be added are the two checkbuttons. These will be used to change some settings of the tree so we can see some of the different options. The both respond to EWL_CALLBACK_CLICKED callbacks the first calling the cb_scroll_headers() function and the second calling cb_scroll_visible() function.

    ewl_main();
    return 0;
}

Finally we call ewl_main() to kick off the main EWL event loop. When ewl_main() is done then we’re finished so just return. ewl_main_quit() will actually call ewl_shutdown() for us so we don’t have to worry about it.

Ok, with our UI out of the way all we need to deal with are all the functions we’ve referenced. First up is creating our data. For this example we’re just using an array but the thing to keep in mind is you can use anything to store your data. EWL never accesses this data directly it always does it through the calls you specified. So, if you want to use an Evas_List or an Ecore_List or a tree or some other structure it’s up to you. EWL doesn’t care.

/* setup our data */
static void *
test_data_setup(void)
{
    Test_Data *data;
    Test_Row_Data **dt;

    data = calloc(1, sizeof(Test_Data));
    dt = calloc(DATA_ELEMENTS, sizeof(Test_Row_Data *));

    dt[0] = calloc(1, sizeof(Test_Row_Data));
    dt[0]->image = strdup("/usr/local/share/ewl/images/e-logo.png");
    dt[0]->text = strdup("The E logo");

    dt[1] = calloc(1, sizeof(Test_Row_Data));
    dt[1]->image = strdup("/usr/local/share/ewl/images/elicit.png");
    dt[1]->text = strdup("The Elicit image");

    dt[2] = calloc(1, sizeof(Test_Row_Data));
    dt[2]->image = strdup("/usr/local/share/ewl/images/entrance.png");
    dt[2]->text = strdup("The Entrance image");

    dt[3] = calloc(1, sizeof(Test_Row_Data));
    dt[3]->image = strdup("/usr/local/share/ewl/images/End.png");
    dt[3]->text = strdup("Zebra");

    dt[4] = calloc(1, sizeof(Test_Row_Data));
    dt[4]->image = strdup("/usr/local/share/ewl/images/banner-top.png");
    dt[4]->text = strdup("Ant");

    data->rows = dt;
    data->count = DATA_ELEMENTS;

    return data;
}

Nothing fancy in there so I’m not going to bother explaining it.

static Ewl_Widget *test_custom_new(void)
{
    Ewl_Widget *button;
    button = ewl_button_new();
    return button;
}

As I mentioned for our third column we’re using a custom constructor and assignment calls. The reason for this, since we just want a simple button, is that we want to set two pieces of data into the widget instead of just one. We could actually just use ewl_button_new() in the view and have a custom assignment function, but this makes for a better example. For the constructor all we do is create our widget and return it. It’s as simple as that.

static void test_custom_assign_set(Ewl_Widget *w, void *data)
{
    Test_Row_Data *d;

    d = data;
    ewl_button_label_set(EWL_BUTTON(w), d->text);
    ewl_button_image_set(EWL_BUTTON(w), d->image, NULL);
}

For the assignment part of the custom widget EWL will provide the widget created with the view, w, and the piece of data that the rows model returned for the current cell in the tree, data. Using these we can setup the widget however we see fit. In this case we’re setting the label and image of the button.

static Ewl_Widget *test_data_header_fetch(void *data , int column)
{
    Ewl_Widget *l;

    l = ewl_label_new();
    if (column == 0)
        ewl_label_text_set(EWL_LABEL(l), "Title");
    else if (column == 1)
        ewl_label_text_set(EWL_LABEL(l), "Image");
    else
        ewl_label_text_set(EWL_LABEL(l), "Button");

    ewl_widget_show(l);

    return l;
}

The last view function we need to worry about is test_data_header_fetch(). EWL will call this function for each column to get the widget to display in the header. The data pointer is the data set on the tree itself and the column is the column number to return the header for (column numbers start at 0).

In this case we’re just creating an Ewl_Label and setting and appropriate bit of text to describe the column.

Not too bad so far, right?

With the view code out of the way lets move on to model code. We start with the fetch function. This is called whenever EWL needs to know what information to pass to the views assign function.

static void *test_data_fetch(void *data, unsigned int row, unsigned int column)
{
    Test_Data *d;
    void *val = NULL;

    d = data;

    if (column == 0)
        val = d->rows[row]->text;
    else if (column == 1)
        val = d->rows[row]->image;
    else if (column == 2)
        val = d->rows[row];

    return val;
}

The fetch function returns a void * as EWL is making no assumptions about what type of data you’ll need to pass into your assign function. In this case we return a char * for the first two columns and a Tree_Row_Data * struct for the third column. The fetch function is passed the data set into the tree, the row and the column that we are interested in.

If you’ve set a sort function into your model then the tree headers become clickable. When the user clicks a header the sort function is called for the given column. With this app I’m just using a simple bubble sort. You’ll possibly want to use something a bit better for a real application.

static void test_data_sort(void *data, unsigned int column, Ewl_Sort_Direction sort)
{
    Test_Data *d;
    int i;

    /* just leave it if we're in sort none. */
    if (sort == EWL_SORT_DIRECTION_NONE)
        return;

    d = data;

    for (i = (DATA_ELEMENTS - 1); i >= 0; i--)
    {
        int j;

        for (j = 1; j <= i; j++)
        {
            char *a, *b;

            if (column == 0)
            {
                a = d->rows[j - 1]->text;
                b = d->rows[j]->text;
            }
            else
            {
                a = d->rows[j - 1]->image;
                b = d->rows[j]->image;
            }

            if (((sort == EWL_SORT_DIRECTION_ASCENDING) && strcmp(a, b) > 0)
                    || ((sort == EWL_SORT_DIRECTION_DESCENDING)
                        && strcmp(a, b) < 0))
            {
                char *temp;

                temp = d->rows[j - 1]->text;
                d->rows[j - 1]->text = d->rows[j]->text;
                d->rows[j]->text = temp;

                temp = d->rows[j - 1]->image;
                d->rows[j - 1]->image = d->rows[j]->image;
                d->rows[j]->image = temp;
            }
        }
    }
}

The sort call has three parameters. The the data we set on the tree, the column number that we are sorting and the direction of the sort. The three possible sort directions are EWL_SORT_DIRECTION_NONE, EWL_SORT_DIRECTION_ASCENDING and EWL_SORT_DIRECTION_DESCENDING. In this case, we don’t bother doing anything if we are set to a sort of EWL_SORT_DIRECTION_NONE. We then sort the data as needed in either ascending or descending order based on the sort parameter.

static int test_data_count_get(void *data)
{
    Test_Data *d;
    d = data;
    return d->count;
}

The last model function we need to implement is the count function. This is just a way for your application to tell EWL how many rows are in your data. In this case we just return the count parameter.

That’s it for the model code. All that’s left are the three callbacks we defined for the UI.

static void cb_delete_window(Ewl_Widget *w, void *ev, void *data)
{
    ewl_widget_destroy(w);
    ewl_main_quit();
}

cb_delete_window() will be called when the window is deleted. All we do is destroy the window with a call to ewl_widget_destroy() and quit the application with ewl_main_quit(). Because we attached this callback to the Ewl_Window widget the first parameter *w is our window. You don’t technically need to ewl_widget_destroy() the window but it’s nice to clean up after yourself.

static void cb_scroll_headers(Ewl_Widget *w, void *ev , void *data)
{
    Ewl_Tree2 *tree;
    tree = data;
    ewl_tree2_scroll_headers_set(tree, ewl_checkbutton_is_checked(EWL_CHECKBUTTON(w)));
}

The cb_scroll_headers() function toggles if the headers in the tree should be scrolled. This is done by calling ewl_tree2_scroll_headers_set() and passing either TRUE or FALSE depending on if we want the headers scrolled. This is conveniently the same as the return of ewl_checkbutton_is_checked().

static void cb_scroll_visible(Ewl_Widget *w, void *ev , void *data)
{
    Ewl_Tree2 *tree;
    tree = data;
    ewl_tree2_scroll_visible_set(tree, ewl_checkbutton_is_checked(EWL_CHECKBUTTON(w)));
}

The cb_scroll_visible() function works in the same way as the cb_scroll_headers() function except that it either enables or disables the display of the scrollbars in the tree.

With that, we’re at the end of our code. If you use the compilation line given above everything should work out and when you run the app you should seeing something similar too:

Hopefully that wasn’t too painful and you can see how easily it is to work with the new Ewl_Tree2 code.

Oh, before I forget. If you’re updating your model and you want EWL to redraw the tree you just need to call ewl_tree2_dirty_set() and pass TRUE as the second parameter. This will signal EWL that something has changed in the model and the tree will be redrawn.