This is probably the most complex example of elementary \hyperlink{group__Genlist}{Genlist (Generic list)}.

We create a tree of items, using the subitems properties of the items, and keep it in memory to be able to expand/hide subitems of an item. The full source code can be found at \hyperlink{genlist_example_05_c}{genlist\+\_\+example\+\_\+05.\+c}

The main point is the way that Genlist manages subitems. Clicking on an item\textquotesingle{}s button to expand it won\textquotesingle{}t really show its children. It will only generate the \char`\"{}expand,request\char`\"{} signal, and the expansion must be done manually.

In this example we want to be able to add items as subitems of another item. If an item has any child, it must be displayed using a parent class, otherwise it will use the normal item class.

It will be possible to delete items too. Once a tree is constructed (with subitems of subitems), and the user clicks on the first parent (root of the tree), the entire subtree must be hidden. However, just calling elm\+\_\+genlist\+\_\+item\+\_\+expanded\+\_\+set(item, E\+I\+N\+A\+\_\+\+F\+A\+L\+S\+E) won\textquotesingle{}t hide them. The only thing that happens is that the parent item will change its appearance to represent that it\textquotesingle{}s contracted. And the signal \char`\"{}contracted\char`\"{} will be emitted from the genlist. Thus, we must call \hyperlink{group__Genlist_ga36fe8144ee138375a9da2886e71da14b}{elm\+\_\+genlist\+\_\+item\+\_\+subitems\+\_\+clear()} to delete all its subitems, but still keep a way to recreate them when expanding the parent again. That\textquotesingle{}s why we are going to keep a node struct for each item, that will be the data of the item, with the following information\+:

 
\begin{DoxyCodeInclude}
\textcolor{keyword}{typedef} \textcolor{keyword}{struct }\_Node\_Data \{
     Eina\_List *children;
     \textcolor{keywordtype}{int} value;
     \textcolor{keywordtype}{int} level;
     Eina\_Bool favorite;
\} Node\_Data;

\end{DoxyCodeInclude}


This {\ttfamily Node\+\_\+\+Data} contains the value for the item, a number indicating its level under the tree, a list of children (to be able to expand it later) and a boolean indicating if it\textquotesingle{}s a favorite item or not.

We use 3 different item classes in this example\+:

One for items that don\textquotesingle{}t have children\+:


\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{int} nitems = 0;

\textcolor{keyword}{static} \textcolor{keywordtype}{char} *
\_item\_label\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   \textcolor{keywordtype}{char} buf[256];
   Node\_Data *d = data;

   \textcolor{keywordflow}{if} (!strcmp(part, \textcolor{stringliteral}{"elm.text"}))
     snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"Item # %i (level %i)"}, d->value, d->level);

   \textcolor{keywordflow}{return} strdup(buf);
\}

\textcolor{keyword}{static} Evas\_Object *
\_item\_content\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   Evas\_Object *ic = elm_icon_add(obj);

   \textcolor{keywordflow}{if} (!strcmp(part, \textcolor{stringliteral}{"elm.swallow.icon"}))
     elm_icon_standard_set(ic, \textcolor{stringliteral}{"file"});

   evas\_object\_size\_hint\_aspect\_set(ic, EVAS\_ASPECT\_CONTROL\_VERTICAL, 1, 1);
   \textcolor{keywordflow}{return} ic;
\}

\end{DoxyCodeInclude}


One for items that have children\+:


\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{char} *
\_parent\_label\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   \textcolor{keywordtype}{char} buf[256];
   Node\_Data *d = data;

   snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"Group %d (%d items)"}, d->value / 7,
            eina\_list\_count(d->children));

   \textcolor{keywordflow}{return} strdup(buf);
\}

\textcolor{keyword}{static} Evas\_Object *
\_parent\_content\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   Evas\_Object *ic = elm_icon_add(obj);

   \textcolor{keywordflow}{if} (!strcmp(part, \textcolor{stringliteral}{"elm.swallow.icon"}))
     elm_icon_standard_set(ic, \textcolor{stringliteral}{"folder"});

   evas\_object\_size\_hint\_aspect\_set(ic, EVAS\_ASPECT\_CONTROL\_VERTICAL, 1, 1);
   \textcolor{keywordflow}{return} ic;
\}

\end{DoxyCodeInclude}


And one for items that were favorited\+:


\begin{DoxyCodeInclude}
\textcolor{keyword}{static} \textcolor{keywordtype}{char} *
\_favorite\_label\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   \textcolor{keywordtype}{char} buf[256];
   Node\_Data *d = data;

   \textcolor{keywordflow}{if} (!strcmp(part, \textcolor{stringliteral}{"elm.text"}))
     snprintf(buf, \textcolor{keyword}{sizeof}(buf), \textcolor{stringliteral}{"Favorite # %i"}, d->value);

   \textcolor{keywordflow}{return} strdup(buf);
\}

\textcolor{keyword}{static} Evas\_Object *
\_favorite\_content\_get(\textcolor{keywordtype}{void} *data, Evas\_Object *obj, \textcolor{keyword}{const} \textcolor{keywordtype}{char} *part)
\{
   Evas\_Object *ic = elm_icon_add(obj);

   \textcolor{keywordflow}{if} (!strcmp(part, \textcolor{stringliteral}{"elm.swallow.icon"}))
     elm_icon_standard_set(ic, \textcolor{stringliteral}{"apps"});

   evas\_object\_size\_hint\_aspect\_set(ic, EVAS\_ASPECT\_CONTROL\_VERTICAL, 1, 1);
   \textcolor{keywordflow}{return} ic;
\}

\end{DoxyCodeInclude}


The favorite item class is there just to demonstrate the \hyperlink{group__Genlist_gac16cbc443fcb23d89039bb7cde138add}{elm\+\_\+genlist\+\_\+item\+\_\+item\+\_\+class\+\_\+update()} function in action. It would be much simpler to implement the favorite behavior by just changing the icon inside the icon\+\_\+get functions when the {\ttfamily favorite} boolean is activated.

Now we are going to declare the callbacks for the buttons that add, delete and change items.

First, a button for appending items to the list\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_append\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Evas\_Object *list = data;
   Elm_Object_Item *glit, *parent = NULL;
   Node\_Data *pdata, *d = malloc(\textcolor{keyword}{sizeof}(*d));

   d->children = NULL;
   d->value = nitems++;
   d->favorite = EINA\_FALSE;

   glit = elm_genlist_selected_item_get(list);
   \textcolor{keywordflow}{if} (glit)
     parent = elm_genlist_item_parent_get(glit);

   \textcolor{keywordflow}{if} (parent)
     \{
        d->level = elm_genlist_item_expanded_depth_get(parent) + 1;
        pdata = elm_object_item_data_get(parent);
        pdata->children = eina\_list\_append(pdata->children, d);
     \}
   \textcolor{keywordflow}{else}
     d->level = 0;

   elm_genlist_item_append(list, \_itc,
                           d, parent,
                           ELM_GENLIST_ITEM_NONE,
                           \_item\_sel\_cb, NULL);
\}

\end{DoxyCodeInclude}


If an item is selected, a new item will be appended to the same level of that item, but using the selected item\textquotesingle{}s parent as its parent too. If no item is selected, the new item will be appended to the root of the tree.

Then the callback for marking an item as favorite\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_favorite\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Evas\_Object *list = data;
   Elm_Object_Item *glit = elm_genlist_selected_item_get(list);

   \textcolor{keywordflow}{if} (!glit) \textcolor{keywordflow}{return};

   Node\_Data *d = elm_object_item_data_get(glit);
   d->favorite = !d->favorite;
   \textcolor{keywordflow}{if} (d->favorite)
     elm_genlist_item_item_class_update(glit, \_itfav);
   \textcolor{keywordflow}{else}
     \{
        \textcolor{keywordflow}{if} (d->children)
          elm_genlist_item_item_class_update(glit, \_itp);
        \textcolor{keywordflow}{else}
          elm_genlist_item_item_class_update(glit, \_itc);
     \}

   elm_genlist_item_update(glit);
\}

\end{DoxyCodeInclude}


This callback is very simple, it just changes the item class of the selected item for the \char`\"{}favorite\char`\"{} one, or go back to the \char`\"{}item\char`\"{} or \char`\"{}parent\char`\"{} class depending on that item having children or not.

Now, the most complex operation (adding a child to an item)\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_add\_child\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Evas\_Object *list = data;
   Elm_Object_Item *glit = elm_genlist_selected_item_get(list);
   Elm_Object_Item *glit\_prev, *glit\_parent;

   \textcolor{keywordflow}{if} (!glit) \textcolor{keywordflow}{return};

   Node\_Data *d = elm_object_item_data_get(glit);
   glit\_prev = elm_genlist_item_prev_get(glit);
   glit\_parent = elm_genlist_item_parent_get(glit);

   Eina\_Bool change\_item = !d->children;

   \textcolor{comment}{// creating new item data}
   Node\_Data *ndata = malloc(\textcolor{keyword}{sizeof}(*ndata));
   ndata->value = nitems++;
   ndata->children = NULL;
   ndata->favorite = EINA\_FALSE;
   ndata->level = elm_genlist_item_expanded_depth_get(glit) + 1;
   d->children = eina\_list\_append(d->children, ndata);

   \textcolor{comment}{// Changing leaf item to parent item}
   \textcolor{keywordflow}{if} (change\_item)
     \{
        elm\_object\_item\_del(glit);

        \textcolor{keywordflow}{if} (glit\_prev != glit\_parent)
          glit = elm_genlist_item_insert_after(list, \_itp, d, glit\_parent,
                                               glit\_prev,
                                               ELM_GENLIST_ITEM_TREE,
                                               \_item\_sel\_cb, NULL);
        \textcolor{keywordflow}{else}
          glit = elm_genlist_item_prepend(list, \_itp, d, glit\_parent,
                                          ELM_GENLIST_ITEM_TREE,
                                          \_item\_sel\_cb, NULL);
        elm_genlist_item_expanded_set(glit, EINA\_FALSE);
        elm_genlist_item_selected_set(glit, EINA\_TRUE);
     \}
   \textcolor{keywordflow}{else} \textcolor{keywordflow}{if} (elm_genlist_item_expanded_get(glit))
     \{
        elm_genlist_item_append(list, \_itc, ndata, glit,
                                ELM_GENLIST_ITEM_NONE, \_item\_sel\_cb, NULL);
     \}

   elm_genlist_item_update(glit);

\}

\end{DoxyCodeInclude}


This function gets the data of the selected item, create a new data (for the item being added), and appends it to the children list of the selected item.

Then we must check if the selected item (let\textquotesingle{}s call it {\ttfamily item1} now) to which the new item (called {\ttfamily item2} from now on) was already a parent item too (using the parent item class) or just a normal item (using the default item class). In the first case, we just have to append the item to the end of the {\ttfamily item1} children list.

However, if the {\ttfamily item1} didn\textquotesingle{}t have any child previously, we have to change it to a parent item now. It would be easy to just change its item class to the parent type, but there\textquotesingle{}s no way to change the item flags and make it be of the type \hyperlink{group__Genlist_gga9727d3b92987319ac981247bc196bd0fae3f8be87b286f57bcd0ebe8e93db665a}{E\+L\+M\+\_\+\+G\+E\+N\+L\+I\+S\+T\+\_\+\+I\+T\+E\+M\+\_\+\+T\+R\+E\+E}. Thus, we have to delete it and create a new item, and add this new item to the same position that the deleted one was. That\textquotesingle{}s the reason of the checks inside the bigger {\ttfamily if}.

After adding the item to the newly converted parent, we set it to not expanded (since we don\textquotesingle{}t want to show the added item immediately) and select it again, since the original item was deleted and no item is selected at the moment.

Finally, let\textquotesingle{}s show the callback for deleting items\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_clear\_list(Node\_Data *d)
\{
   Node\_Data *tmp;

   EINA\_LIST\_FREE(d->children, tmp)
      \_clear\_list(tmp);
   free(d);
\}

static \textcolor{keywordtype}{void}
\_del\_item\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Evas\_Object *list = data;
   Elm_Object_Item *glit = elm_genlist_selected_item_get(list);
   Elm_Object_Item *glit\_parent = NULL;

   \textcolor{keywordflow}{if} (!glit) \textcolor{keywordflow}{return};

   Node\_Data *pdata, *d = elm_object_item_data_get(glit);
   glit\_parent = elm_genlist_item_parent_get(glit);
   elm_genlist_item_subitems_clear(glit);
   elm\_object\_item\_del(glit);

   \_clear\_list(d);

   \textcolor{keywordflow}{if} (!glit\_parent) \textcolor{keywordflow}{return};

   pdata = elm_object_item_data_get(glit\_parent);
   pdata->children = eina\_list\_remove(pdata->children, d);
   elm_genlist_item_update(glit\_parent);
\}

\end{DoxyCodeInclude}


Since we have an iternal list representing each element of our tree, once we delete an item we have to go deleting each child of that item, in our internal list. That\textquotesingle{}s why we have the function {\ttfamily \+\_\+clear\+\_\+list}, which recursively goes freeing all the item data.

This is necessary because only when we really want to delete the item is when we need to delete the item data. When we are just contracting the item, we need to hide the children by deleting them, but keeping the item data.

Now there are two callbacks that will be called whenever the user clicks on the expand/contract icon of the item. They will just request to items to be contracted or expanded\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_expand\_request\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Elm_Object_Item *glit = event\_info;
   printf(\textcolor{stringliteral}{"expand request on item: %p\(\backslash\)n"}, event\_info);
   elm_genlist_item_expanded_set(glit, EINA\_TRUE);
\}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_contract\_request\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Elm_Object_Item *glit = event\_info;
   printf(\textcolor{stringliteral}{"contract request on item: %p\(\backslash\)n"}, event\_info);
   elm_genlist_item_expanded_set(glit, EINA\_FALSE);
\}

\end{DoxyCodeInclude}


When the \hyperlink{group__Genlist_ga2ded3582b3c3ceb4f90ff6c1def80727}{elm\+\_\+genlist\+\_\+item\+\_\+expanded\+\_\+set()} function is called with {\ttfamily E\+I\+N\+A\+\_\+\+T\+R\+U\+E}, the {\ttfamily \+\_\+expanded\+\_\+cb} will be called. And when this happens, the subtree of that item must be recreated again. This is done using the internal list stored as item data for each item. The function code follows\+:


\begin{DoxyCodeInclude}

\textcolor{keyword}{static} \textcolor{keywordtype}{void}
\_expanded\_cb(\textcolor{keywordtype}{void} *data, Evas\_Object *o, \textcolor{keywordtype}{void} *event\_info)
\{
   Eina\_List *l;
   Elm_Object_Item *glit = event\_info;
   Node\_Data *it\_data, *d = elm_object_item_data_get(glit);
   Evas\_Object *list = elm\_object\_item\_widget\_get(glit);

   Elm_Genlist_Item_Class *ic;

   EINA\_LIST\_FOREACH(d->children, l, it\_data)
     \{
        Elm_Object_Item *nitem;
        Elm_Genlist_Item_Type type = ELM_GENLIST_ITEM_NONE;
        printf(\textcolor{stringliteral}{"expanding item: #%d from parent #%d\(\backslash\)n"}, it\_data->value, d->value);
        \textcolor{keywordflow}{if} (it\_data->favorite)
          ic = \_itfav;
        \textcolor{keywordflow}{else} \textcolor{keywordflow}{if} (it\_data->children)
          \{
             ic = \_itp;
             type = ELM_GENLIST_ITEM_TREE;
          \}

\end{DoxyCodeInclude}


Each appended item is set to contracted, so we don\textquotesingle{}t have to deal with checking if the item was contracted or expanded before its parent being contracted. It could be easily implemented, though, by adding a flag expanded inside the item data.

Now, the {\ttfamily \+\_\+contracted\+\_\+cb}, which is much simpler\+:


\begin{DoxyCodeInclude}
        \textcolor{keywordflow}{else}
          ic = \_itc;

        nitem = elm_genlist_item_append(list, ic, it\_data, glit,
                                        type, \_item\_sel\_cb, NULL);
        elm_genlist_item_expanded_set(nitem, EINA\_FALSE);
     \}

\end{DoxyCodeInclude}


We just have to call \hyperlink{group__Genlist_ga36fe8144ee138375a9da2886e71da14b}{elm\+\_\+genlist\+\_\+item\+\_\+subitems\+\_\+clear()}, that will take care of deleting every item, and keep the item data still stored (since we don\textquotesingle{}t have any del function set on any of our item classes).

Finally, the code inside {\ttfamily elm\+\_\+main} is very similar to the other examples\+:


\begin{DoxyCodeInclude}
elm\_main(\textcolor{keywordtype}{int} argc, \textcolor{keywordtype}{char} **argv)
\{
   Evas\_Object *win, *box, *fbox;
   Evas\_Object *list;
   \textcolor{keywordtype}{int} i;

   win = elm_win_util_standard_add(\textcolor{stringliteral}{"genlist"}, \textcolor{stringliteral}{"Genlist"});
   elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
   elm_win_autodel_set(win, EINA\_TRUE);

   box = elm_box_add(win);
   evas\_object\_size\_hint\_weight\_set(box, EVAS\_HINT\_EXPAND, EVAS\_HINT\_EXPAND);
   elm_win_resize_object_add(win, box);
   evas\_object\_show(box);

   \textcolor{keywordflow}{if} (!\_itc)
     \{
        \_itc = elm_genlist_item_class_new();
        \_itc->item\_style = \textcolor{stringliteral}{"default"};
        \_itc->func.text\_get = \_item\_label\_get;
        \_itc->func.content\_get = \_item\_content\_get;
        \_itc->func.state\_get = NULL;
        \_itc->func.del = NULL;
     \}

   \textcolor{keywordflow}{if} (!\_itp)
     \{
        \_itp = elm_genlist_item_class_new();
        \_itp->item\_style = \textcolor{stringliteral}{"default"};
        \_itp->func.text\_get = \_parent\_label\_get;
        \_itp->func.content\_get = \_parent\_content\_get;
        \_itp->func.state\_get = NULL;
        \_itp->func.del = NULL;
     \}

   \textcolor{keywordflow}{if} (!\_itfav)
     \{
        \_itfav = elm_genlist_item_class_new();
        \_itfav->item\_style = \textcolor{stringliteral}{"default"};
        \_itfav->func.text\_get = \_favorite\_label\_get;
        \_itfav->func.content\_get = \_favorite\_content\_get;
        \_itfav->func.state\_get = NULL;
        \_itfav->func.del = NULL;
     \}

   list = elm_genlist_add(win);

   evas\_object\_size\_hint\_weight\_set(list, EVAS\_HINT\_EXPAND, EVAS\_HINT\_EXPAND);
   evas\_object\_size\_hint\_align\_set(list, EVAS\_HINT\_FILL, EVAS\_HINT\_FILL);
   elm_box_pack_end(box, list);
   evas\_object\_show(list);

   fbox = elm_box_add(win);
   elm_box_layout_set(fbox, evas\_object\_box\_layout\_flow\_horizontal,
                      NULL, NULL);
   evas\_object\_size\_hint\_weight\_set(fbox, EVAS\_HINT\_EXPAND, 0);
   evas\_object\_size\_hint\_align\_set(fbox, EVAS\_HINT\_FILL, EVAS\_HINT\_FILL);
   elm_box_pack_end(box, fbox);
   evas\_object\_show(fbox);

   \_button\_add(list, fbox, \textcolor{stringliteral}{"append item"}, \_append\_cb);
   \_button\_add(list, fbox, \textcolor{stringliteral}{"favorite"}, \_favorite\_cb);
   \_button\_add(list, fbox, \textcolor{stringliteral}{"add child"}, \_add\_child\_cb);
   \_button\_add(list, fbox, \textcolor{stringliteral}{"del item"}, \_del\_item\_cb);

   \textcolor{keywordflow}{for} (i = 0; i < N\_ITEMS; i++)
     \{
        Elm_Object_Item *gli = NULL, *glg = NULL;
        Node\_Data *data = malloc(\textcolor{keyword}{sizeof}(*data)); \textcolor{comment}{// data for this item}
        data->children = NULL;
        data->value = i;
        data->favorite = EINA\_FALSE;
        nitems++;

        Node\_Data *pdata = NULL; \textcolor{comment}{// data for the parent of the group}

        printf(\textcolor{stringliteral}{"creating item: #%d\(\backslash\)n"}, data->value);
        \textcolor{keywordflow}{if} (i % 3 == 0)
          \{
             glg = gli = elm_genlist_item_append(list, \_itp, data, NULL,
                                                 ELM_GENLIST_ITEM_TREE,
                                                 \_item\_sel\_cb, NULL);
             elm_genlist_item_expanded_set(glg, EINA\_TRUE);
             pdata = data;
             data->level = 0;
          \}
        \textcolor{keywordflow}{else}
          \{
             gli = elm_genlist_item_append(list, \_itc, data, glg,
                                           ELM_GENLIST_ITEM_NONE,
                                           \_item\_sel\_cb, NULL);
             \textcolor{keywordflow}{if} (pdata)
               pdata->children = eina\_list\_append(pdata->children, data);
             data->level = 1;
          \}
     \}

   evas\_object\_smart\_callback\_add(list, \textcolor{stringliteral}{"expand,request"}, \_expand\_request\_cb, list);
   evas\_object\_smart\_callback\_add(list, \textcolor{stringliteral}{"contract,request"}, \_contract\_request\_cb, list);
   evas\_object\_smart\_callback\_add(list, \textcolor{stringliteral}{"expanded"}, \_expanded\_cb, list);
   evas\_object\_smart\_callback\_add(list, \textcolor{stringliteral}{"contracted"}, \_contracted\_cb, list);

   evas\_object\_resize(win, 420, 600);
   evas\_object\_show(win);

   elm_run();

   \textcolor{keywordflow}{return} 0;
\}
ELM_MAIN()

\end{DoxyCodeInclude}


The example will look like this when running\+:

 
\begin{DoxyImageNoCaption}
  \mbox{\includegraphics[width=\textwidth]{screenshots/genlist_example_05}}
\end{DoxyImageNoCaption}
 