Bundles Sample Overview

Mobile native

The Bundle sample demonstrates how to create and manipulate key-value data objects. In this application, a bundle object use example is shown for data exchange through a message port.

The following figure illustrates the main screens of the application.

Figure: Bundle application screens

Plain data source view Filled data source view Data sink view

The sample application provides a user interface for:

  • Creating a bundle object: 1 bundle object is created and filled with user-defined key-value pairs.
  • Sending a bundle object: the created bundle object is sent through a message port.

The structure of the user interface is depicted in the following figures. EDJE layout scripts are used in the UI.

Figure: Bundle UI layout structure of the data source view

Bundle UI layout structure

Bundle UI component structure

Figure: Bundle UI layout structure of the data sink view

Bundle UI layout structure

Bundle UI component structure

The application workflow can be divided into the following logical pipelines:

  • Bundle application startup
  • Bundle data creation
  • Bundle object sending
  • Bundle object receiving

The following figure describes the workflow.

Figure: Bundle application workflow

Application workflow - startup Application workflow - data creation Application workflow - object sending Application workflow - object sending Application workflow - object structure

Implementation

Type Definitions

The main data structure is used as a placeholder for the model and view data:

typedef struct appdata
{
   // View module data
   viewdata_s view;	
   // Model module data
   modeldata_s model;	
} appdata_s;

The viewdata_s structure contains references to all component objects created by the View module:

typedef struct 
{
   // The main window object
   Evas_Object* win;		
   // The conformant object
   Evas_Object* conform;
   // The main window's layout object (embedded into the conform component)		
   Evas_Object* layout_main_panel;
   // The toolbar object (embedded into the PART_MAIN_TOOLBAR part of 
   // the layout_main_panel object)			
   Evas_Object* main_toolbar;
   // The data source item of the main_toolbar component
   Elm_Object_Item *main_toolbar_item_data_source;
   // The data sink item of the main_toolbar component
   Elm_Object_Item *main_toolbar_item_data_sink;	
   // The data source view layout (embedded into the PART_MAIN_CONTENT part 
   // of the layout_main_panel object)
   Evas_Object* layout_data_source;
   // The data source edit panel layout (embedded into the PART_MAIN_CONTENT 
   // part of the layout_main_panel object)
   Evas_Object* layout_data_source_edit;
   // The key name entry component (embedded into the PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY 
   // part of the layout_data_source_edit object)
   Evas_Object* data_source_key_entry;
   // The value entry component (embedded into the PART_DATA_SOURCE_EDIT_PANEL_VALUE_PANEL_ENTRY 
   // part of the layout_data_source_edit object)
   Evas_Object* data_source_value_entry;
   // The value type selector component (embedded into the PART_DATA_SOURCE_EDIT_PANEL_TYPE_PANEL_ENTRY 
   // part of the layout_data_source_edit object)
   Evas_Object* data_source_type_selector;
   // The data source list panel layout (embedded into the PART_DATA_SOURCE_LIST_PANEL 
   // part of the layout_data_source object)
   Evas_Object* layout_data_source_list;
   // The header inclusion check component (embedded into the PART_DATA_SOURCE_LIST_PANEL_CHECKBOX 
   // part of the layout_data_source_list object)
   Evas_Object* data_source_checkbox;
   // The list component (embedded into the PART_DATA_SOURCE_LIST_PANEL_LIST part of the 
   // layout_data_source_list object)
   Evas_Object* data_source_list;
   // The data source buttons panel layout (embedded into the PART_DATA_SOURCE_BUTTONS_PANEL 
   // part of the layout_data_source object)
   Evas_Object* layout_data_source_buttons;
   // The "Send" button component (embedded into the PART_DATA_SOURCE_BUTTONS_PANEL_SEND 
   // part of the layout_data_source_buttons object)
   Evas_Object* data_source_button_send;
   // The "Add" button component (embedded into the PART_DATA_SOURCE_BUTTONS_PANEL_ADD 
   // part of the layout_data_source_buttons object)
   Evas_Object* data_source_button_add;
   // The data sink view layout (embedded into the PART_MAIN_CONTENT part of the 
   // layout_main_panel object)
   Evas_Object* layout_data_sink;
   // The message entry component (embedded into the PART_DATA_SINK_ENTRY part of the 
   // layout_data_sink object)
   Evas_Object* sink_entry;			
} viewdata_s;

The modeldata_s structure contains a list of bundledata_s items, where each of the bundledata_s structures contains data to be included in the bundle object. Additionally, the identifier of the message port for data receiving is declared.

typedef struct _modeldata 
{
   // The list that consists of bundledata_s(user input data: key name, value, value type)
   Eina_List *items_list;
   // The identifier of the local message port
   int msg_port_rcv_id;	
} modeldata_s;

The bundledata_s structure is used as a list item of the modeldata_s->items_list structure:

typedef struct _bundledata 
{
   // The key name provided by the user
   char *key;
   // The value provided by the user
   void *value;
   // The value type selected by the user
   bundle_value_type_t type;
} bundledata_s;

The bundle_value_type_t is an enumeration type:

typedef enum 
{
   // Value type of the byte
   BUNDLE_VALUE_TYPE_BYTE,
   // Value type of the string	
   BUNDLE_VALUE_TYPE_STRING,
   // Points to the end of the enumeration type
   BUNDLE_VALUE_TYPE_MAX		
} bundle_value_type_t;

Application Initialization

The entire application life-cycle is implemented in the main.c file, using the common Tizen application structure:

int
main(int argc, char *argv[])
{
   appdata_s ad = {{0,},};
   // Declare and initialize the variables

   event_callback.create = app_create;
   event_callback.terminate = app_terminate;
   event_callback.pause = app_pause;
   event_callback.resume = app_resume;
   event_callback.app_control = app_control;

   // Assign the event handlers

   ret = ui_app_main(argc, argv, &event_callback, &ad);
   // Error handling

   return ret;
}

The Bundle sample application is implemented using the MVC design pattern. Its initialization is done within the app_create() callback function where the controller_initialization() function is responsible for the application initialization. On the application termination, the app_terminate() callback function is called, and all the allocated resources are freed. For reference and more details, see Controller.

static bool
app_create(void *data)
{
   // Assign the variables

   appdata_s *ad = (appdata_s*)data;

   return controller_initialization(&d->view, &ad->model);
}

static void
app_terminate(void *data)
{
   appdata_s *ad = (appdata_s*)data;

   controller_terminate(&ad->view, &ad->model);
}

View

The entire application layout is implemented using EDJE scripts. All top level swallows are designed for EFL Elementary component embedding. The following EDJE swallow - EFL Elementary component relations and assigned functionalities are used:

  • PART_MAIN_TOOLBAR-elm_toolbar: Switches the view between the data source and the data sink.
  • PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY-elm_entry: Key name edit field.
  • PART_DATA_SOURCE_EDIT_PANEL_VALUE_PANEL_ENTRY-elm_entry: Value edit field.
  • PART_DATA_SOURCE_EDIT_PANEL_TYPE_PANEL_ENTRY-elm_spinner: Sets the value type.
  • PART_DATA_SOURCE_LIST_PANEL_CHECKBOX-elm_check: Prepends a custom header to the bundle object.
  • PART_DATA_SOURCE_LIST_PANEL_LIST-elm_genlist: Displays the list of all key-value pairs to be added to the bundle object.

    The list item consists of:

    • Key name and value displayed as key: value
    • Type of the value displayed as type: type-name
  • PART_DATA_SOURCE_BUTTONS_PANEL_ADD-elm_button: Adds a key-value pair to the list.
  • PART_DATA_SOURCE_BUTTONS_PANEL_SEND-elm_button: Creates a bundle object and sends it to the data sink.
  • PART_DATA_SINK_ENTRY-elm_entry: Displays the received data.

For more information, see also the Bundle UI data source layout structure and Bundle UI data sink layout structure figures.

The following code snippets create the application layout.

Table: UI layout code snippets and figures
Code snippet Figure
The main layout is defined in the main.edc file.
collections 
{
   group 
   {
      name: GROUP_MAIN;

      parts 
      {
         // The background part occupies the entire window
         part 
         {
            name: PART_MAIN_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to PART_MAIN_BACKGROUND
         // The spacer occupies the entire area of PART_MAIN_BACKGROUND 
         // with a small margin all around
         part 
         {
            name: PART_MAIN_PANEL;
            type: SPACER;
         }

         // The part is positioned in relation to PART_MAIN_PANEL
         // The swallow occupies 6% of PART_MAIN_PANEL height
         // It is designed to hold the elm_toolbar component
         part 
         {
            name: PART_MAIN_TOOLBAR;
            type: SWALLOW;
         }

         // The part is positioned in relation to PART_MAIN_PANEL
         // The swallow occupies 93% of PART_MAIN_PANEL height
         // It is designed to hold the data source/data sink layout
         part 
         {
            name: PART_MAIN_CONTENT;
            type: SWALLOW;
         }
      }
   }
}

EDJE main layout

The PART_MAIN_CONTENT swallow is used as a container for:

The data source and data sink layouts are switched by the toolbar item selection.

collections 
{
   group 
   {
      name: GROUP_DATA_SOURCE;

      parts 
      {
         // The part is positioned in relation to PART_MAIN_CONTENT 
         // from the main.edc file
         // The rect plays a role of the background for the edit panel 
         // and occupies the entire area
         // of the PART_MAIN_CONTENT
         part
         {
            name: PART_DATA_SOURCE_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_BACKGROUND
         // The swallow part occupies 40% height and 100% width of the 
         // PART_DATA_SOURCE_BACKGROUND
         // It is designed to hold the data source edit layout defined in 
         // data_source_edit_panel.edc
         part 
         {
            name: PART_DATA_SOURCE_EDIT_PANEL;
            type: SWALLOW;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_BACKGROUND
         // The swallow part occupies 50% height and 100% width of the 
         // PART_DATA_SOURCE_BACKGROUND
         // It is designed to hold the data source list layout defined in 
         // data_source_list_panel.edc
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL;
            type: SWALLOW;
         }

         // The part is positioned in relation to the 
         // PART_DATA_SOURCE_BACKGROUND
         // The swallow part occupies 10% height and 100% width of the 
         // PART_DATA_SOURCE_BACKGROUND
         // It is designed to hold the data source buttons layout defined in 
         // data_source_buttons_panel.edc
         part 
         {
            name: PART_DATA_SOURCE_BUTTONS_PANEL;
            type: SWALLOW;
         }
      }
   }
}

EDJE data source layout

The PART_DATA_SOURCE_EDIT_PANEL swallow is used as a container for data edit (input) layout defined in the data_source_edit_panel.edc file (for more information, see Data source panel layout).
collections 
{
   group 
   {
      name: GROUP_DATA_SOURCE_EDIT_PANEL;

      parts 
      {
         // The part is positioned in relation to 
         // PART_DATA_SOURCE_EDIT_PANEL from the data_source.edc file
         // The rect plays a role of the background for the edit panel 
         // and occupies the entire area
         // of the PART_DATA_SOURCE_EDIT_PANEL
         part 
         {
            name: PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND;
            type: RECT;
         }

         // ----------=============== KEY NAME INPUT PANEL ===============----------

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND
         // The swallow part occupies 33% height and 100% width of the
         // PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND. It is designed to 
         // organize the key name editing area
         part 
         {
            name: PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL;
            type: SPACER;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL
         // The text part occupies 100% height and 33% width of the
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL
         // This part is responsible for static text label display 
         // only ("Key name")
         part 
         {
            name: "data_source_edit_panel_key_panel_label";
            type: TEXT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL
         // The rect plays a role of a background for the elm_entry 
         // component. Its size is set to
         // 70% width and 70% height of the 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL. 
         // This part is vertically aligned
         part 
         {
            name: PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY_BACKGROUND
         // The swallow part occupies the entire area of the 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY_BACKGROUND
         // It is designed to hold elm_entry component for key name input
         part 
         {
            name: PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY;
            type: SWALLOW;
         }

         // ----------=============== VALUE INPUT PANEL ===============----------

         // The layout of the PART_DATA_SOURCE_EDIT_PANEL_VALUE_PANEL 
         // part is exactly the same as the layout of the 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL part
         // The only difference is that its vertical location is set 
         // to the 33% of PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND height
         // For this reason, the source code is not listed here


         // ----------=============== VALUE TYPE INPUT PANEL ===============----------

         // The layout of the PART_DATA_SOURCE_EDIT_PANEL_TYPE_PANEL part 
         // is exactly the same as the layout of the 
         // PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL part
         // There are only 2 differences:
         // 1. Its height is set to 34% of the 
         // PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND height
         // 2. Its vertical location is set to the 66% of the
         // PART_DATA_SOURCE_EDIT_PANEL_BACKGROUND height
         // For this reason, the source code is not listed here
      }
   }
}

EDJE data source edit layout

The PART_DATA_SOURCE_LIST_PANEL swallow is used as a container for the data source list layout defined in the data_source_list_panel.edc file (for more information, see Data source panel layout).
collections 
{
   group 
   {
      name: GROUP_DATA_SOURCE_LIST_PANEL;

      parts 
      {
         // The part is positioned in relation to 
         // PART_DATA_SOURCE_LIST_PANEL from the data_source.edc file
         // The rect plays a role of the background for the list panel 
         // and occupies the entire area of the 
         // PART_DATA_SOURCE_LIST_PANEL
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_LIST_PANEL_BACKGROUND
         // The spacer part occupies 15% height and 100% width of the
         // PART_DATA_SOURCE_LIST_PANEL_BACKGROUND
         // It is designed to organize bundle header inclusion checkbox area
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL;
            type: SPACER;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         // The rect plays a role of a background for the elm_check component 
         // Its size is set to 9,7% width and 80% height of the 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         // This part is vertically aligned. Its horizontal position is 
         // set to 60% width of the 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to the
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         // The text part occupies 80% height and 60% width of the
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         // This part is vertically aligned and is responsible for static
         // text label display only ("Include bundle header")
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_LABEL;
            type: TEXT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL.
         // The swallow part occupies 80% height and 40% width of the 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_PANEL
         // It is aligned next to the right border of the 
         // PART_DATA_SOURCE_LIST_PANEL_CHECKBOX_LABEL and designed 
         // to hold the elm_check component for bundle header inclusion
         part
         {
            name: PART_DATA_SOURCE_LIST_PANEL_CHECKBOX;
            type: SWALLOW;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_LIST_PANEL_BACKGROUND
         // The swallow part occupies 100% width and 85% height of the
         // PART_DATA_SOURCE_LIST_PANEL_BACKGROUND. It is aligned to the 
         // bottom border of the related part. It is designed to hold 
         // the elm_genlist component for input data display
         part 
         {
            name: PART_DATA_SOURCE_LIST_PANEL_LIST;
            type: SWALLOW;
         }
      }
   }
}

EDJE data source list layout

The PART_DATA_SOURCE_BUTTONS_PANEL swallow is used as a container for the data source buttons layout defined in the data_source_buttons_panel.edc file (for more information, see Data source panel layout).
collections 
{
   group 
   {
      name: GROUP_DATA_SOURCE_BUTTONS_PANEL;

      parts 
      {
         // The part is positioned in relation to 
         // PART_DATA_SOURCE_BUTTONS_PANEL from data_source.edc file
         // The rect plays a role of the background for the buttons 
         // panel and occupies the entire area of the 
         // PART_DATA_SOURCE_BUTTONS_PANEL
         part 
         {
            name: PART_DATA_SOURCE_BUTTONS_PANEL_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SOURCE_BUTTONS_PANEL_BACKGROUND
         // The swallow part occupies 80% height and 46% width of the 
         // PART_DATA_SOURCE_BUTTONS_PANEL_BACKGROUND
         // Its left border is set to 2% width of related container 
         // Vertically, the swallow is centered
         // This part is designed to hold the elm_button component for input data
         // addition to the data list
         part 
         {
            name: PART_DATA_SOURCE_BUTTONS_PANEL_ADD;
            type: SWALLOW;
         }

         // The part relations and sizing are the same as described 
         // above, for PART_DATA_SOURCE_BUTTONS_PANEL_ADD part
         // The only difference is the left border positioning, 
         // which is set to 52% width of the 
         // PART_DATA_SOURCE_BUTTONS_PANEL_BACKGROUND part
         // This part is designed to hold the elm_button component for bundle 
         // sending
         part 
         {
            name: PART_DATA_SOURCE_BUTTONS_PANEL_SEND;
            type: SWALLOW;
         }
      }
   }
}

EDJE data source buttons layout

The PART_MAIN_CONTENT swallow is used as a container for the data sink layout defined in the data_sink.edc file (for more information, see Main panel layout).
collections 
{
   group 
   {
      name: GROUP_DATA_SINK;

      parts 
      {
         // The part is positioned in relation to PART_MAIN_CONTENT 
         // from main.edc file
         // The rect plays a role of the background for the entry panel 
         // and occupies the entire area of the PART_MAIN_CONTENT
         part 
         {
            name: PART_DATA_SINK_BACKGROUND;
            type: RECT;
         }

         // The part is positioned in relation to 
         // PART_DATA_SINK_BACKGROUND
         // The swallow part occupies the entire area of 
         // PART_DATA_SINK_BACKGROUND
         // This part is designed to hold the elm_entry component for 
         // received data display
         part 
         {
            name: PART_DATA_SINK_ENTRY;
            type: SWALLOW;
         }
      }
   }
}

EDJE data sink layout

Based on the layout defined with EDJE scripts, the application interface is created with the view_base_gui_create() function. The function takes 1 parameter, a pointer to the structure containing the view data. The view_base_gui_create() function is invoked in the controller_initialization() function called from the app_create() callback function. For the call stack details, see Application Initialization. The following code snippet presents the general steps in the user interface creation.

bool
view_base_gui_create(viewdata_s *vd)
{
   // The pointer to the viewdata_s structure is stored for future use
   // The variable viewdata is declared globally in the scope of view.c file
   viewdata = vd;

   // Main panel view creation (window, conformant, main layout, toolbar)
   if (!view_main_panel_create(vd)) 
   {
      // Error handling
   }

   // Data source view creation
   if (!view_data_source_content_create(vd)) 
   {
      // Error handling
   }

   // Data sink view creation.
   if (!view_data_sink_content_create(vd)) 
   {
      // Error handling
   }

   // The data source layout object is assigned to the data field of the 
   // "Data source" toolbar's item
   // This layout object is used in view_toolbar_item_selected_cb() callback 
   // function to switch between the layouts depending on toolbar's item selection
   elm_object_item_data_set(vd->main_toolbar_item_data_source, (void*)vd->layout_data_source);
   elm_object_item_data_set(vd->main_toolbar_item_data_sink, (void*)vd->layout_data_sink);

   // "Data source" tab is marked as selected (the view_toolbar_item_selected_cb()
   // callback function is not called automatically)
   elm_toolbar_item_selected_set(vd->main_toolbar_item_data_source, EINA_TRUE);

   // Set the vd->layout_data_source object as the content of viewdata->layout_main_panel
   // layout because the view_toolbar_item_selected_cb() callback function is not called
   // when elm_toolbar_item_selected_set() is invoked
   elm_object_part_content_set(viewdata->layout_main_panel, PART_MAIN_CONTENT, vd->layout_data_source);

   evas_object_show(vd->win);

   return true;
}

The entire application view creation is triggered by the view_base_gui_create() function described above. The result of the succeeding subfunction invocations is depicted in the following table.

Table: Base view creation code snippets and figures
Description Code snippet Figure

view_main_panel_create():

The main window and descendant conformant (vd->win and vd->conform respectively) are created and used as a placeholder for the main layout (vd->layout_main_panel).

The main layout is created with the view_generic_layout_create_set() function by loading the main group from the EDJE layout (main.edj file), then it is embedded into the vd->layout_main_panel container. Finally, the view_layout_back_cb() callback function is attached to the vd->layout_main_panel for the hardware Back button handling.

When the EDJE layout is successfully loaded, the elm_toolbar component can be created as a descendant of the main layout vd->layout_main_panel.

At the end, 2 items are appended to the newly created elm_toolbar component with the view_toolbar_item_selected_cb() callback function attached:

The view_toolbar_item_selected_cb() callback function is responsible for switching between the layout objects embedded to the PART_MAIN_CONTENT swallow of the vd->layout_main_panel. Those layouts are defined in the data_source.edc and data_sink.edc files.

static bool
view_main_panel_create(viewdata_s *vd)
{
   vd->win = view_generic_window_create(view_win_delete_request_cb);
   // Error handling

   vd->conform = view_generic_conformant_create(vd->win);
   // Error handling

   vd->layout_main_panel = view_generic_layout_create_set(vd->conform, 
                                                          EDJ_MAIN_FILE_NAME, 
                                                          GROUP_MAIN, 
                                                          NULL);
   // Error handling
   eext_object_event_callback_add(vd->layout_main_panel, EEXT_CALLBACK_BACK, 
                                  view_layout_back_cb, (void*)vd);

   vd->main_toolbar = view_generic_toolbar_create(vd->layout_main_panel, 
                                                  PART_MAIN_TOOLBAR);
   // Error handling

   vd->main_toolbar_item_data_source = elm_toolbar_item_append(vd->main_toolbar, 
                                                               NULL, 
                                                               "Data source", 
                                                               view_toolbar_item_selected_cb, 
                                                               NULL);
   // Error handling

   vd->main_toolbar_item_data_sink = elm_toolbar_item_append(vd->main_toolbar, 
                                                             NULL, 
                                                             "Data sink", 
                                                             view_toolbar_item_selected_cb, 
                                                             NULL);
   // Error handling

   return true;
}

UI main layout

view_data_source_content_create():

The entire data source view is created by the data source layout loading from the data_source.edj file.

After the layout is loaded, the following subviews are created:

  • Data source edit subview: view_data_source_edit_create()
  • Data source list subview: view_data_source_list_create()
  • Data source buttons subview: view_data_source_buttons_create()

The final layout is not inserted into the PART_MAIN_CONTENT as this operation is performed later, depending on toolbar item selection.

The side figure depicts the vd->layout_data_source layout only.

static bool
view_data_source_content_create(viewdata_s *vd)
{
   vd->layout_data_source = view_generic_layout_create(vd->layout_main_panel, 
                                                       EDJ_DATA_SOURCE_FILE_NAME, 
                                                       GROUP_DATA_SOURCE);
   // Error handling

   if (!view_data_source_edit_create(vd)) 
   {
      return false;
   }

   if (!view_data_source_list_create(vd)) 
   {
      return false;
   }

   if (!view_data_source_buttons_create(vd)) 
   {
      return false;
   }

   return true;
}

UI data source layout

view_data_source_edit_create():

The vd->layout_data_source_edit layout is created based on data_source_edit_panel.edc. The resulting layout is embedded into the PART_DATA_SOURCE_PANEL.

The elm_entry and elm_spinner components are created next and inserted into the vd->layout_data_source_edit layout. The newly created components are used for the bundle key name input (value and type, respectively). The elm_spinner component is then filled with the values reflecting all the available types of the key's value.

static bool
view_data_source_edit_create(viewdata_s *vd)
{
   // Error handling

   vd->layout_data_source_edit = view_generic_layout_create_set(vd->layout_data_source,
                                                                EDJ_DATA_SOURCE_EDIT_PANEL_FILE_NAME,
                                                                GROUP_DATA_SOURCE_EDIT_PANEL,
                                                                PART_DATA_SOURCE_EDIT_PANEL);
   // Error handling

   vd->data_source_key_entry = view_generic_entry_create(vd->layout_data_source_edit,
                                                         PART_DATA_SOURCE_EDIT_PANEL_KEY_PANEL_ENTRY);
   // Error handling

   vd->data_source_value_entry = view_generic_entry_create(vd->layout_data_source_edit,
                                                           PART_DATA_SOURCE_EDIT_PANEL_VALUE_PANEL_ENTRY);
   // Error handling

   vd->data_source_type_selector = view_generic_spinner_create(vd->layout_data_source_edit,
                                                               PART_DATA_SOURCE_EDIT_PANEL_TYPE_PANEL_ENTRY);
   // Error handling

   elm_spinner_min_max_set(vd->data_source_type_selector, 0, (int)BUNDLE_VALUE_TYPE_MAX-1);

   int i;
   for (i = 0; i < BUNDLE_VALUE_TYPE_MAX; i++) 
   {
      elm_spinner_special_value_add(vd->data_source_type_selector, i, bundletypes[i].caption);
   }

   return true;
}

UI data source edit layout

view_data_source_list_create():

The vd->layout_data_source_list layout is created based on data_source_list_panel.edc. The resulting layout is embedded into the PART_DATA_SOURCE_LIST_PANEL.

The elm_check and the elm_genlist components are created next and inserted into the vd->layout_data_source_list layout. After the elm_check component is created, the view_checkbox_changed_cb() callback function is assigned to it in order to handle the component's state change event. For the implementation details of the view_checkbox_changed_cb() callback function, see User Interaction.

static bool
view_data_source_list_create(viewdata_s *vd)
{
   // Error handling

   vd->layout_data_source_list = view_generic_layout_create_set(vd->layout_data_source,
                                                                EDJ_DATA_SOURCE_LIST_PANEL_FILE_NAME,
                                                                GROUP_DATA_SOURCE_LIST_PANEL,
                                                                PART_DATA_SOURCE_LIST_PANEL);
   // Error handling

   vd->data_source_checkbox = view_generic_checkbox_create(vd->layout_data_source_list,
                                                           PART_DATA_SOURCE_LIST_PANEL_CHECKBOX,
                                                           "");
   // Error handling
   evas_object_smart_callback_add(vd->data_source_checkbox, "changed",
                                  view_checkbox_changed_cb, (void*)vd);

   vd->data_source_list = view_generic_genlist_create(vd->layout_data_source_list,
                                                      PART_DATA_SOURCE_LIST_PANEL_LIST);
   // Error handling

   return true;
}

UI data source list layout

view_data_source_buttons_create():

The vd->layout_data_source_buttons layout is created based on data_source_buttons_panel.edc. The resulting layout is embedded into the PART_DATA_SOURCE_BUTTONS_PANEL.

The 2 elm_button components are created next and inserted into the vd->layout_data_source_buttons layout. To each of the buttons created (vd->data_source_button_add, vd->data_source_button_send), the relevant callback function is attached for handling the click event:

  • view_button_add_clicked_cb(): handles input data adding to the list.
  • view_button_send_clicked_cb(): handles bundle creation and sending through the message port.

For the implementation details of the callback functions, see User Interaction.

static bool
view_data_source_buttons_create(viewdata_s *vd)
{
   // Error handling

   vd->layout_data_source_buttons = view_generic_layout_create_set(vd->layout_data_source,
                                                                   EDJ_DATA_SOURCE_BUTTONS_PANEL_FILE_NAME,
                                                                   GROUP_DATA_SOURCE_BUTTONS_PANEL,
                                                                   PART_DATA_SOURCE_BUTTONS_PANEL);
   // Error handling

   vd->data_source_button_add = view_generic_button_create(vd->layout_data_source_buttons,
                                                           PART_DATA_SOURCE_BUTTONS_PANEL_ADD,
                                                           "Add",
                                                           view_button_add_clicked_cb,
                                                           (void*)vd);
   // Error handling

   vd->data_source_button_send = view_generic_button_create(vd->layout_data_source_buttons,
                                                            PART_DATA_SOURCE_BUTTONS_PANEL_SEND,
                                                            "Send",
                                                            view_button_send_clicked_cb,
                                                            (void*)vd);
   // Error handling

   return true;
}

UI data source button layout

view_data_sink_content_create():

The vd->layout_data_sink layout is created based on data_sink.edc. The resulting layout is embedded into the PART_MAIN_CONTENT.

The elm_entry component is created next and inserted into the vd->layout_data_sink layout. The elm_entry component plays the role of a non-editable text panel for the received messages display.

static bool
view_data_sink_content_create(viewdata_s *vd)
{
   vd->layout_data_sink = view_generic_layout_create(vd->layout_main_panel,
                                                     EDJ_DATA_SINK_FILE_NAME,
                                                     GROUP_DATA_SINK);
   // Error handling

   vd->sink_entry = view_generic_entry_create(vd->layout_data_sink,
                                              PART_DATA_SINK_ENTRY);
   // Error handling

   elm_entry_single_line_set(vd->sink_entry, EINA_FALSE);
   elm_entry_editable_set(vd->sink_entry, EINA_FALSE);
   elm_entry_scrollable_set(vd->sink_entry, EINA_TRUE);

   return true;
}

UI data sink layout

User Interaction

The interaction between the user and the sample application must follow the following scheme:

  • Inputting data (key name, key value) and selecting the data type using the provided components.
  • Adding input data to the data list using the Add button. This action can be performed multiple times for different sets of input data.
  • Optionally selecting the bundle header for inclusion using the provided check component.
  • Bundling the data list and sending it to the data sink through a message port using the Send button.

After the message is successfully sent, the user can switch the view from the data source to the data sink, where the received data is printed in a text form.

Inputting Data and Adding to the List

Input data addition is triggered by the Add button click:

  1. As a result of the button click, the view_button_add_clicked_cb() callback function is invoked.
    static void
    view_button_add_clicked_cb(void *data, Evas_Object *obj, void *event_info)
    {
       viewdata_s *vd = (viewdata_s*)data;
       bundledata_s *bundledata = NULL;
    
       // Error handling
    
       // Gather the input data and pack it to the bundledata_s structure
       if (!view_input_data_to_bundledata(vd, &bundledata)) 
       {
          return;
       }
    
       // If the bundledata is successfully obtained, it is added to the data list
       model_list_item_add(bundledata);
    
       // Append the bundledata content to the elm_genlist component
       view_genlist_item_append(bundledata);
    }
    
  2. Within the view_button_add_clicked_cb() callback function, the input data provided by the user is gathered from the UI and packed into the bundledata_s structure (view_input_data_to_bundledata()) which is then added to the data list using the model_list_item_add() function.

    For the bundledata_s type specification, see Type Definitions.

    static bool
    view_input_data_to_bundledata(viewdata_s *vd, bundledata_s **bundledata)
    {
       // Declare the variables
    
       if (!view_input_data_get(vd, &key, &str_value, &value_type)) 
       {
          return false;
       }
    
       if (view_input_data_value_pointer_get(str_value, value_type, &ptr_value, &val_size)) 
       {
          *bundledata = model_bundledata_create(key, ptr_value, val_size, value_type);
       }
    
       free(key);
       free(str_value);
    
       return (*bundledata != NULL);
    }
    

    Within the view_input_data_to_bundledata() function, the view_input_data_get() function acquires all necessary data from the user interface and performs its validation:

    static bool
    view_input_data_get(viewdata_s *vd, char **key, char **value, bundle_value_type_t *value_type)
    {
       // Variable declaration, initialization, and error handling
    
       *key = view_key_string_get(vd);
       *value = view_value_string_get(vd);
       *value_type = (bundle_value_type_t)view_type_index_get(vd);
     
       // Check whether acquired data are valid in terms of pointers and value type range correctness
       ret = (*key && *value && *value_type >= BUNDLE_VALUE_TYPE_BYTE && *value_type < BUNDLE_VALUE_TYPE_MAX);
    
       if (!ret) 
       {
       // Free the memory consumed by *key and *value
       }
    
       return ret;
    }
    

    Within the view_input_data_to_bundledata() function, the view_input_data_value_pointer_get() function's implementation is also omitted. It converts the str_value string to the relevant data type, based on the value_type set by the user, and assigns it to the provided ptr_value of void* type. The size of the data, referenced by the ptr_value, is returned in the val_size parameter. When all the data (key, value reference, and value type) are successfully acquired, the model_bundledata_create() function is called to create the bundledata_s structure (for implementation details, see Model). Otherwise, the view_input_data_to_bundledata() function fails and frees all previously allocated memory.

    After the view_input_data_to_bundledata() function successfully returns and the bundledata_s structure is created, the model_list_item_add() function is invoked (refer to the view_button_add_clicked_cb() callback function) to store the structure for future use in a list of bundledata_s items. For the implementation details of the model_list_item_add() function, see Model.

  3. The obtained input data is appended to the elm_genlist using the view_genlist_item_append() function.

    To access the UI components directly, 3 simple functions are used: view_key_string_get(), view_value_string_get(), view_type_index_get(). Due to the implementation simplicity of the mentioned functions, they are not listed here.

    static Elm_Object_Item*
    view_genlist_item_append(bundledata_s *bundledata)
    {
       // Error handling
    
       Elm_Genlist_Item_Class* itc = view_generic_genlist_item_class_create(view_genlist_item_label_get_cb, view_genlist_item_del_cb);
       // Error handling
    
       Elm_Object_Item *item = elm_genlist_item_append(viewdata->data_source_list, itc, (void*)bundledata,
                                                       NULL, ELM_GENLIST_ITEM_NONE, NULL, NULL);
       // Error handling
    
       elm_genlist_item_bring_in(item, ELM_GENLIST_ITEM_SCROLLTO_TOP);
    
       return item;
    }
    

    The view_generic_genlist_item_class_create() function creates the genlist item class representing the visual style of all the items. The callback functions, passed as parameters (view_genlist_item_label_get_cb() and view_genlist_item_del_cb()), are used to control the display and release of the bundledata attached to the itc using the elm_genlist_item_append() function.

    Elm_Genlist_Item_Class*
    view_generic_genlist_item_class_create(Elm_Genlist_Item_Text_Get_Cb on_text_get_cb, Elm_Genlist_Item_Del_Cb on_item_del_cb)
    {
       static Elm_Genlist_Item_Class *itc = NULL;
    
       if (!itc) 
       {
          itc = elm_genlist_item_class_new();
          // Error handling
    
          itc->item_style = "double_label";
          itc->func.text_get = on_text_get_cb;
          // NULL value assignment to unused itc fields
          itc->func.del = on_item_del_cb;
       }
    
       return itc;
    }
    

    Within the view_genlist_item_append() function, the itc class together with the previously created bundledata_s structure are passed as parameters to the elm_genlist_item_append() function. As a result, a new item representing the user input data is appended to the elm_genlist component. The elm_genlist is scrolled so the newly appended item becomes visible (elm_genlist_item_bring_in()).

Including the Bundle Header

The user can add 1 additional key-value pair to the bundle, representing the data header (refer to the Bundle object structure depicted in the Bundle application workflow figure). By checking the Include bundle header checkbox, the elm_genlist component is updated with an additional item. The "real" header is added to the bundle object in the bundle creation phase (see Bundling and Sending the Data List). The Include bundle header checkbox state change results in the view_checkbox_changed_cb() callback function invocation.

Bundling and Sending the Data List

When the user clicks Send, the view_button_send_clicked_cb() callback is triggered and creates the bundle object (with respect to the state of the Include bundle header checkbox). Afterwards, the bundle is sent using a message port.

static void
view_button_send_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   // Error handling

   viewdata_s *vd = (viewdata_s*)data;

   // Obtain the state of the Include bundle header checkbox
   bool include_header = (bool)elm_check_state_get(vd->data_source_checkbox);

   // Bundle the bundledata_s structures created earlier and send them using a message port
   controller_data_source_message_send(include_header);
}

For the implementation details of the controller_data_source_message_send() function, see Data Source Controller.

Controller

The general Controller module handles the application initialization and termination procedures. For more information on the invocation context of the functions, see Application Initialization.

The controller_initialization() function handles:

  • Model data initialization: model_data_create() (see Model).
  • User interface creation: view_base_gui_create() (see View).
  • Data sink creation and initialization: controller_data_sink_create() (see Data Sink Controller).

If any of the above functions fail, the controller_initialization() function returns a failure status which closes the application.

bool
controller_initialization(viewdata_s *vd, modeldata_s *md)
{
   return (model_data_create(md) &&
           view_base_gui_create(vd) &&
           controller_data_sink_create());
}

When the application is terminated, the controller_terminate() function is called. It is responsible for freeing all the allocated memory, deleting the user interface, and releasing the related resources. For more information about the used functions, see Data Sink Controller, View, and Model.

void
controller_terminate(viewdata_s *vd, modeldata_s *md)
{
   controller_data_sink_destroy();
   view_base_gui_destroy(vd);
   model_data_destroy(md);
}

Data Source Controller

The data source controller module is responsible for data bundling and sending using a message port. The function triggering the entire process (controller_data_source_message_send()) is referenced in Bundling and Sending the Data List.

  1. All user input data stored in a list is obtained simply by referencing the Eina_List object, declared in the Model module, in the model_data_list_get() function.
  2. The empty bundle object is created with the model_bundle_create() function (for reference, see Bundle Model).
  3. The data addition to the bundle object starts with the controller_data_source_bundle_header_create() function call.

    The function adds the HEADER key with 0 or 1 value to the bundle_msg bundle object. The 0 or 1 value is assigned depending on the include_header variable value. If the include_header == 1, the additional HEADER_DATA key is added to the bundle_msg bundle object. The value type of the HEADER_DATA key is an array of strings, where all the cells of the array are filled with key names defined by the user during the data input procedure (for reference, see Inputting Data and Adding to the List).

  4. To finalize the data addition process, all the user-defined key-value pairs are appended to the bundle object with the controller_data_source_bundle_data_add() function.
  5. Once the bundle object is successfully created and populated with the data, it is sent through the message port using the model_message_port_message_send() function (for reference, see Message Port Model).
  6. After the bundle_msg is sent, it is not needed anymore, so it can be deleted with the model_bundle_destroy() function (for reference, see Bundle Model).
bool
controller_data_source_message_send(bool include_header)
{
   Eina_List *bundle_data_list = NULL;
   bundle *bundle_msg = NULL;

   if (!model_data_list_get(&bundle_data_list)) 
   {
      return false;
   }

   if (!model_bundle_create(&bundle_msg)) 
   {
      return false;
   }

   if (!controller_data_source_bundle_header_create(&bundle_msg, include_header)) 
   {
      model_bundle_destroy(bundle_msg);

      return false;
   }

   if (!controller_data_source_bundle_data_add(bundle_msg, bundle_data_list)) 
   {
      model_bundle_destroy(bundle_msg);

      return false;
   }

   bool ret = model_message_port_message_send(bundle_msg);

   model_bundle_destroy(bundle_msg);

   return ret;
}

The following table shows a bundle object structure preview example. Assume that the bundle contains 3 user-defined keys with the following values:

  • Key name: "EX-STR-1", value: "exemplary-string-1", type: string
  • Key name: "EX-INT-2", value: 1, type: byte
  • Key name: "EX-STR-3", value: "exemplary-string-2", type: string
Table: Bundle object data
include_header == 0 include_header == 1
HEADER   : 0
EX-STR-1 : "exemplary-string-1"
EX-INT-2 : 1
EX-STR-3 : "exemplary-string-2"



HEADER          : 1
HEADER_DATA [0] : "EX-STR-1"
            [1] : "EX-INT-2"
            [2] : "EX-STR-3"
EX-STR-1        : "exemplary-string-1"
EX-INT-2        : 1
EX-STR-3        : "exemplary-string-2"

The call-stack order of the function listing is preserved.

static bool
controller_data_source_bundle_header_create(bundle **bundle_obj, bool include_header)
{
   // Declare the variables

   // Error handling

   // Add the BUNDLE_HEADER_KEY with assigned value of "include_header" to the bundle object
   if (!model_bundle_byte_add(bundle_obj, BUNDLE_HEADER_KEY, (int)include_header)) 
   {
      return false;
   }

   // If the user did not include the bundle header, the function returns
   // Otherwise, the header data is appended
   if (!include_header) 
   {
      return true;
   }

   // Get all the user-defined key-value pairs in the form of a list containing bundledata_s structures
   if (!model_data_list_get(&bundle_data_list)) 
   {
      return false;
   }

   items_count = eina_list_count(bundle_data_list);
   if (items_count == 0) 
   {
      return true;
   }

   // Create the array and fill it with the key names defined by the user
   key_array = (char**)malloc(sizeof(char*) * items_count);
   // Error handling

   EINA_LIST_FOREACH(bundle_data_list, tmp, bundledata) 
   {
      key_array[i] = strdup(bundledata->key);
      i++;
   }

   // Add the created array of user-defined key names to the bundle object
   bool ret = model_bundle_string_array_add(bundle_obj, BUNDLE_HEADER_DATA_KEY, key_array, items_count);

   // Free the key_array's content and the array

   return ret;
}

static bool
controller_data_source_bundle_data_add(bundle *bundle_obj, Eina_List *bundle_data_list)
{
   // Declare the variables

   // Error handling

   // Append each list's item of type bundledata_s to the bundle object
   EINA_LIST_FOREACH(bundle_data_list, tmp, bundledata) 
   {
      controller_data_source_bundle_data_append(bundle_obj, bundledata);
   }

   return true;
}

static bool
controller_data_source_bundle_data_append(bundle *bundle_obj, bundledata_s *bundledata)
{
   // Error handling

   // Call the appropriate function depending on the key's value type (byte/string) to
   // add the key-value pair to the bundle object
   switch (bundledata->type) 
   {
      case BUNDLE_VALUE_TYPE_BYTE:
         if (!model_bundle_byte_add(&bundle_obj, bundledata->key, *((int*)bundledata->value))) 
         {
            return false;
         }
         break;
      case BUNDLE_VALUE_TYPE_STRING:
         if (!model_bundle_string_add(&bundle_obj, bundledata->key, (char*)bundledata->value)) 
         {
            return false;
         }
         break;
      default:
         // Error handling
   }

   return true;
}

Data Sink Controller

The data sink controller module is responsible for the initialization and finalization of the communication channel using a message port and receiving messages. The data sink initialization function (controller_data_sink_create()) is invoked from the controller_initialization() function contained in the general Controller module. Similarly, the finalization function (controller_data_sink_destroy()) is called from the controller_terminate() function contained in the same general Controller module.

In the message port initialization procedure, a new communication channel is created with the controller_data_sink_message_received_cb() callback function attached. When a new message arrives, the callback function is invoked and the received data passed in the callback.

To create the data sink:

bool
controller_data_sink_create(void)
{
   // Check whether the message port already exists
   if (model_message_port_exists_check()) 
   {
      return true;
   }

   // Create a new message port if it does not exist yet
   return model_message_port_create(controller_data_sink_message_received_cb);
}

To destroy the data sink and release the resources:

bool
controller_data_sink_destroy(void)
{
   // If the message port was never created, there is nothing to do
   if (!model_message_port_exists_check()) 
   {
      return true;
   }

   // Close the communication channel and the message port
   return model_message_port_destroy();
}

When a new message arrives through the created message port, the controller_data_sink_message_received_cb() callback function is invoked. The general approach of data extraction from the received bundle object is based on the knowledge about its structure (see the bundle object structure depicted in the Bundle application workflow figure). The workflow can be described with the following steps:

  1. Get the number of bundled items.
  2. Get the value of BUNDLE_HEADER_KEY:
    • If the value of BUNDLE_HEADER_KEY equals 1, the data header is included and must be extracted using the model_bundle_string_array_get() function. As a result, the string array is returned.
    • If the value of BUNDLE_HEADER_KEY equals 0, the data header is not included.
  3. If the bundle data header exists, it is printed to the data sink view.
  4. Bundle data enumeration is performed using the model_bundle_foreach() function with the controller_data_sink_bundle_foreach_cb() callback function attached.

For the description of all model-related functions, see Bundle Model.

static void
controller_data_sink_message_received_cb(int local_port_id, const char *remote_app_id, const char *remote_port, 
                                         bool trusted_remote_port, bundle *message, void *user_data)
{
   // Declare the variables
   // Error handling
   // Print the initial message to the data sink view

   // Obtain the number of items contained in received bundle object
   if (model_bundle_count_get(message, &items_count)) 
   {
      // Format the text message
   }

   // Print the number of bundle items to the data sink view

   // Get the value of BUNDLE_HEADER_KEY to verify the existence of the data header
   if (!model_bundle_byte_get(message, BUNDLE_HEADER_KEY, &header_value)) 
   {
      return;
   }

   // Print the message to the data sink view

   if ((bool)header_value) 
   {
      // If the data header exists, the strings array value attached to the BUNDLE_HEADER_DATA_KEY is obtained
      if (!model_bundle_string_array_get(message, BUNDLE_HEADER_DATA_KEY, &header_data, &header_size)) 
      {
         return;
      }

      // All strings contained in the header_data and attached to the BUNDLE_HEADER_DATA_KEY 
      // are enumerated and printed to the data sink view
   } 
   else 
   {
      // Print the message to the data sink view
   }

   // Print the message to the data sink view

   // Bundled data item enumeration
   if (!model_bundle_foreach(message, controller_data_sink_bundle_foreach_cb)) 
   {
      // Print the message to the data sink view
   }
}

Once the data header is decoded and extracted, the enumeration of data items starts (model_bundle_foreach()). For each bundled item, the controller_data_sink_bundle_foreach_cb() callback function is invoked.

static void
controller_data_sink_bundle_foreach_cb(const char *key, const int type, const bundle_keyval_t *kv, void *user_data)
{
   // Declare the variables and initialize

   // If this callback function is invoked for any of the data header related keys 
   // (BUNDLE_HEADER_KEY, BUNDLE_HEADER_DATA_KEY), the function returns
   if (controller_same_string_check(key, BUNDLE_HEADER_KEY) || controller_same_string_check(key, BUNDLE_HEADER_DATA_KEY)) 
   {
      return;
   }

   // Get the value's type of the enumerated bundle's item
   if (model_bundle_keyval_type_get(kv, &value_type)) 
   {
      // The numeric type code is converted into the string name
   }

   // Get the value of the enumerated bundle's item
   if (model_bundle_keyval_basic_val_get(kv, &value)) 
   {
      // Format the text message
   }

   // Print the message to the data sink view
}

The following table shows the text printed to the data sink view in the case shown in Data Source Controller.

Table: Text printed in the data sink view
include_header == 0 include_header == 1
---=== MSG RECEIVED ===---
Bundle items count: 4
Header data:
     - N/A
Data:
     - EX-STR-1 = exemplary-string-1 (string)
     - EX-INT-2 = 1 (byte)
     - EX-STR-3 = exemplary-string-2 (string)


---=== MSG RECEIVED ===---
Bundle items count: 5
Header data:
     - Key: EX-STR-1 (string)
     - Key: EX-INT-2 (byte)
     - Key: EX-STR-3 (string)
Data:
     - EX-STR-1 = exemplary-string-1 (string)
     - EX-INT-2 = 1 (byte)
     - EX-STR-3 = exemplary-string-2 (string)

Model

The general Model module deals directly with the application data. It is responsible for:

  • Model initialization and finalization
  • User data list handling

In the initialization step, the model_data_create() function is invoked from the controller_initialization() function. The model_data_destroy() function is called in the application termination phase by the controller_terminate() function. For the call stack reference, see Controller.

To create the model:

bool
model_data_create(modeldata_s *md)
{
   // Error handling

   // The pointer to the modeldata_s structure is locally stored
   modeldata = md;

   return true;
}

To destroy the model and free the resources:

void
model_data_destroy(modeldata_s *md)
{
   // Error handling

   // Free the content of the input data list
   if (md->items_list) 
   {
      md->items_list = eina_list_free(md->items_list);
   }
}

The user input data are added to the list with the model_bundledata_create() function referenced from the view_input_data_to_bundledata() function, which is called by the view_button_add_clicked_cb() callback function on the Add button press. For reference, see Inputting Data and Adding to the List.

bundledata_s*
model_bundledata_create(char *key, void *ptr_value, int val_size, bundle_value_type_t value_type)
{
   // Error handling

   bundledata_s *bundledata = (bundledata_s*)malloc(sizeof(bundledata_s));
   // Error handling

   bundledata->key = strdup(key);
   bundledata->value = (void*)malloc(val_size);
   memcpy(bundledata->value, ptr_value, val_size);
   bundledata->type = value_type;

   return bundledata;
}

Once the user input data structure (bundledata_s described in Type Definitions) is created, it can be added to the list with the model_list_item_add() function and accessed with the model_data_list_get() function. The first function is invoked from the view_button_add_clicked_cb() callback function (see Inputting Data and Adding to the List) and the second one is called during the data bundling and sending procedure (controller_data_source_message_send() referenced in Data Source Controller). The implementation of both functions is very simple and limited to proper Eina_List function invocations. For this reason, they are not listed here.

Bundle Model

The Bundle model module provides a set of wrapper functions for the Bundle API used by the Controller module for the bundle management:

  • Creating and removing the bundle object:
    bool
    model_bundle_create(bundle **bundle_obj)
    {
       // Create the bundle object
       *bundle_obj = bundle_create();
       // Error handling
    
       return true;
    }
    
    bool
    model_bundle_destroy(bundle *bundle_obj)
    {
       // Free the bundle object
       int ret = bundle_free(bundle_obj);
       // Error handling
    
       return true;
    }
    
  • Extracting the information on bundled data:
    • Number of bundled items:
      bool
      model_bundle_count_get(bundle *bundle_obj, int *count)
      {
         // The number of bundled items is acquired if the value returned is non-negative
         // Otherwise, the error code is returned
         *count = bundle_get_count(bundle_obj);
         int ret = get_last_result();
         // Error handling
      
         return true;
      }
      
    • Value type:
      bool
      model_bundle_type_get(bundle *bundle_obj, const char *key, int *type)
      {
         // The type of the bundled item's value, assigned to the given key, is acquired if the value returned is non-negative
         // Otherwise, the error code is returned
         *type = bundle_get_type(bundle_obj, key);
         int ret = get_last_result();
         // Error handling
      
         return true;
      }
      
  • Adding the data to the bundle with respect to its type:
    • String value:

      bool
      model_bundle_string_add(bundle **bundle_obj, const char *key, const char *value)
      {
         // The string value assigned to the given key is added to the bundle
         int ret = bundle_add_str(*bundle_obj, key, value);
         // Error handling
      
         return true;
      }
      
    • Array of string values:

      bool
      model_bundle_string_array_add(bundle **bundle_obj, const char *key, char **value, int value_count)
      {
         // Error handling
      
         // The array of string values assigned to the given key is added to the bundle
         int ret = bundle_add_str_array(*bundle_obj, key, (const char**)value, value_count);
         // Error handling
      
         return true;
      }
      
    • Numerical value:

      bool
      model_bundle_byte_add(bundle **bundle_obj, const char *key, int value)
      {
         // The numerical value assigned to the given key is added to the bundle
         int ret = bundle_add_byte(*bundle_obj, key, &value, sizeof(value));
         // Error handling
      
         return true;
      }
      
  • Extracting the bundled data with respect to its type:
    • Retrieve a string value:

      bool
      model_bundle_string_get(bundle *bundle_obj, const char *key, char **value)
      {
         *value = NULL;
      
         // The string value assigned to the given key is acquired from the bundle
         int ret = bundle_get_str(bundle_obj, key, value);
         // Error handling
      
         return true;
      }
      
    • Retrieve string array values:

      bool
      model_bundle_string_array_get(bundle *bundle_obj, const char *key, const char ***value, int *value_len)
      {
         *value_len = 0;
      
         // The string array value assigned to the given key is acquired from the bundle
         *value = bundle_get_str_array(bundle_obj, key, value_len);
         int ret = get_last_result();
         // Error handling
      
         return true;
      }
      
    • Retrieve a numerical value:

      bool
      model_bundle_byte_get(bundle *bundle_obj, const char *key, int *value)
      {
         *value = 0;
      
         void *byte_val = NULL;
         size_t size_val = 0;
      
         // The numerical value assigned to the given key is acquired from the bundle in a form of void pointer
         int ret = bundle_get_byte(bundle_obj, key, &byte_val, &size_val);
         // Error handling
      
         // Returned value referenced by the void pointer is copied to the variable of integer type
         if (size_val > 0 && size_val <= sizeof(int)) 
         {
            *value = *((int*)byte_val);
         }
      
         return true;
      }
      
    • Retrieve the value type:

      bool
      model_bundle_keyval_type_get(const bundle_keyval_t *kv, int *type)
      {
         // The type of the bundled item's value is acquired
         // This function is used within the callback function invoked by the bundle_foreach() function
         // If the returned value is non-negative, it points to the value type. Otherwise, the error code is returned
         *type = bundle_keyval_get_type((bundle_keyval_t*)kv);
         int ret = get_last_result();
         // Error handling
      
         return true;
      }
      
    • Retrieve the bundled value:

      bool
      model_bundle_keyval_basic_val_get(const bundle_keyval_t *kv, void **value)
      {
         size_t value_size = 0;
         // The bundled value is acquired. This function is used within the callback function invoked by the bundle_foreach() function
         int ret = bundle_keyval_get_basic_val((bundle_keyval_t*)kv, value, &value_size);
         // Error handling
      
         return true;
      }
      
  • Bundled item enumeration:
    bool
    model_bundle_foreach(bundle *bundle_obj, bundle_iterator_t func_cb)
    {
       // Error handling
    
       // Bundled items are enumerated and the func_cb callback function is called for each item contained in the bundle
       bundle_foreach(bundle_obj, func_cb, NULL);
    
       return true;
    }
    

Message Port Model

The message port model module provides a set of wrapper functions for the Message Port API used by the Data Sink Controller and Data Source Controller modules:

  • Create and remove the message port:
    bool
    model_message_port_create(message_port_message_cb func_cb)
    {
       // The local message port is registered with a func_cb callback function assigned
       // The func_cb is called whenever a message is received
       // If the returned value is non-negative, the message port is created successfully
       // Otherwise, the ret value points to the error code
       int ret = message_port_register_local_port(MESSAGE_PORT_RCV_NAME, func_cb, NULL);
       // Error handling
    
       // The identifier of a message port is stored
       model_data_get()->msg_port_rcv_id = ret;
    
       return true;
    }
    
    bool
    model_message_port_destroy(void)
    {
       // The message port is unregistered based on its identifier
       int ret = message_port_unregister_local_port(model_data_get()->msg_port_rcv_id);
       // Error handling
    
       // The identifier of a message port is cleared
       model_data_get()->msg_port_rcv_id = 0;
    
       return true;
    }
    
  • Check the message port existence:
    bool
    model_message_port_exists_check(void)
    {
       // Check whether the message port identifier is non-negative
       // If so, the message port exists
       return (model_data_get()->msg_port_rcv_id > 0);
    }
    
  • Send the bundle object:
    bool
    model_message_port_message_send(bundle *message)
    {
       // The bundled message is sent over the message port previously created
       int ret = message_port_send_message(PACKAGE, MESSAGE_PORT_RCV_NAME, message);
       // Error handling
    
       return true;
    }