rt-thread/components/rtgui/common/rtgui_mv_model.c

455 lines
12 KiB
C

/*
* File : rtgui_mv_model.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2012, RT-Thread Development Team
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rt-thread.org/license/LICENSE
*
* Change Logs:
* Date Author Notes
* 2012-09-15 Grissiom first version
*/
#include <rtgui/rtgui.h>
#include <rtgui/rtgui_app.h>
#include <rtgui/rtgui_object.h>
#include <rtgui/rtgui_system.h>
#include <rtgui/rtgui_mv_model.h>
#include <rtgui/widgets/mv_view.h>
#include <rtgui/widgets/window.h>
static void _rtgui_mv_model_bare_remove(struct rtgui_mv_model *model, struct rtgui_mv_view *view);
static void _rtgui_mv_view_bare_remove(struct rtgui_mv_view *view, struct rtgui_mv_model *model);
static rt_bool_t _rtgui_mv_model_notify_view(struct rtgui_mv_model *model,
struct rtgui_mv_view *view,
struct rtgui_event_mv_model *emodel);
static void _rtgui_mv_model_constructor(struct rtgui_mv_model *model)
{
model->dimension = 0;
model->length = 0;
model->data = RT_NULL;
model->view_number = 0;
model->view = RT_NULL;
/* currently not interested in any event */
rtgui_object_set_event_handler(RTGUI_OBJECT(model), RT_NULL);
}
static void _rtgui_mv_model_destructor(struct rtgui_mv_model *model)
{
if (model->view_number == 1)
{
rtgui_mv_model_remove_view(model, model->view);
}
else if (model->view_number > 1)
{
int i;
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
for (i = 0; i < model->view_number; i++)
{
rtgui_mv_model_remove_view(model, view_arr[i]);
}
}
if (model->dimension > 1)
rtgui_free(model->data);
}
DEFINE_CLASS_TYPE(mv_model, "mv_model",
RTGUI_OBJECT_TYPE,
_rtgui_mv_model_constructor,
_rtgui_mv_model_destructor,
sizeof(struct rtgui_mv_model));
struct rtgui_mv_model *rtgui_mv_model_create(rt_uint16_t dimension)
{
struct rtgui_mv_model *model;
RT_ASSERT(dimension);
model = RTGUI_MV_MODEL(rtgui_object_create(RTGUI_MV_MODEL_TYPE));
if (model == RT_NULL)
return RT_NULL;
if (rtgui_mv_model_set_dimension(model, dimension) != RT_EOK)
{
rtgui_object_destroy(RTGUI_OBJECT(model));
return RT_NULL;
}
return model;
}
RTM_EXPORT(rtgui_mv_model_create);
rt_err_t rtgui_mv_model_set_dimension(struct rtgui_mv_model *model, rt_uint16_t dimension)
{
if (dimension == 1)
{
if (model->dimension > 1)
rtgui_free(model->data);
model->data = RT_NULL;
}
else
{
void *data;
if (model->dimension > 1)
rtgui_free(model->data);
data = rtgui_malloc(sizeof(void *)*dimension);
if (data == RT_NULL)
{
return -RT_ENOMEM;
}
rt_memset(data, 0, sizeof(void *)*dimension);
model->data = data;
}
model->dimension = dimension;
return RT_EOK;
}
RTM_EXPORT(rtgui_mv_model_set_dimension);
void rtgui_mv_model_destroy(struct rtgui_mv_model *model)
{
rtgui_object_destroy(RTGUI_OBJECT(model));
}
RTM_EXPORT(rtgui_mv_model_destroy);
rt_err_t rtgui_mv_model_add_view(struct rtgui_mv_model *model, struct rtgui_mv_view *view)
{
RT_ASSERT(model);
if (view == RT_NULL)
return RT_EOK;
/* add view to model */
if (model->view_number == 0)
{
model->view = view;
}
else if (model->view_number == 1)
{
/* create the array of view pointers */
struct rtgui_mv_view **new_view;
if (view == model->view)
return RT_EOK;
new_view = rtgui_malloc(2 * sizeof(struct rtgui_mv_view *));
if (new_view == RT_NULL)
return -RT_ENOMEM;
new_view[0] = model->view;
new_view[1] = view;
model->view = new_view;
}
else
{
int i;
struct rtgui_mv_view **new_view;
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
for (i = 0; i < model->view_number; i++)
{
if (view == view_arr[i])
return RT_EOK;
}
new_view = rtgui_realloc(model->view,
(model->view_number + 1) * sizeof(struct rtgui_mv_view *));
if (new_view == RT_NULL)
return -RT_ENOMEM;
new_view[model->view_number] = view;
model->view = new_view;
}
model->view_number++;
/* add model to view. Roll back previous action on fail. */
if (view->model_number == 0)
{
view->model = model;
}
else if (view->model_number == 1)
{
struct rtgui_mv_model **new_arr = rtgui_malloc(2 * sizeof(struct rtgui_mv_model *));
if (new_arr == RT_NULL)
{
_rtgui_mv_model_bare_remove(model, view);
return -RT_ENOMEM;
}
new_arr[0] = view->model;
new_arr[1] = model;
view->model = new_arr;
}
else
{
struct rtgui_mv_model **new_arr = rtgui_realloc(view->model,
(view->model_number + 1) * sizeof(struct rtgui_mv_model *));
if (new_arr == RT_NULL)
{
_rtgui_mv_model_bare_remove(model, view);
return -RT_ENOMEM;
}
new_arr[view->model_number] = model;
view->model = new_arr;
}
view->model_number++;
return RT_EOK;
}
RTM_EXPORT(rtgui_mv_model_add_view);
static void _rtgui_mv_model_bare_remove(struct rtgui_mv_model *model, struct rtgui_mv_view *view)
{
int i;
if (model->view_number == 1)
{
if (model->view == view)
{
model->view_number--;
model->view = RT_NULL;
}
return;
}
for (i = 0; i < model->view_number; i++)
{
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
if (view == view_arr[i])
break;
}
/* no match */
if (i == model->view_number)
{
return;
}
else if (model->view_number == 2)
{
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
struct rtgui_mv_view *the_view = view_arr[(i + 1) % 2];
rtgui_free(model->view);
model->view = the_view;
model->view_number--;
return;
}
else
{
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
void *new_view;
for (; i < model->view_number - 1; i++)
{
view_arr[i] = view_arr[i + 1];
}
new_view = rtgui_realloc(model->view,
(model->view_number - 1) * sizeof(struct rtgui_mv_view *));
/* no need to be panic on OOM error. The original array is still there
* and we can operate it safely. So just ignore the NULL value returned
* by realloc. */
if (new_view != RT_NULL)
model->view = new_view;
model->view_number--;
return;
}
}
static void _rtgui_mv_view_bare_remove(struct rtgui_mv_view *view, struct rtgui_mv_model *model)
{
int i;
struct rtgui_mv_model **model_arr = (struct rtgui_mv_model **)(view)->model;
if (view->model_number == 1)
{
if (view->model == model)
{
view->model_number--;
view->model = RT_NULL;
}
return;
}
for (i = 0; i < view->model_number; i++)
{
if (model == model_arr[i])
break;
}
/* no match */
if (i == view->model_number)
{
return;
}
else if (view->model_number == 2)
{
struct rtgui_mv_model **model_arr = (struct rtgui_mv_model **)(view)->model;
struct rtgui_mv_model *the_model = model_arr[(i + 1) % 2];
rtgui_free(view->model);
view->model = the_model;
view->model_number--;
return;
}
else
{
struct rtgui_mv_model **model_arr = (struct rtgui_mv_model **)(view)->model;
void *new_model;
for (; i < view->model_number - 1; i++)
{
model_arr[i] = model_arr[i + 1];
}
new_model = rtgui_realloc(view->model,
(view->model_number - 1) * sizeof(struct rtgui_mv_model *));
/* no need to be panic on OOM error. The original array is still there
* and we can operate it safely. So just ignore the NULL value returned
* by realloc. */
if (new_model != RT_NULL)
view->model = new_model;
view->model_number--;
return;
}
}
void rtgui_mv_model_remove_view(struct rtgui_mv_model *model, struct rtgui_mv_view *view)
{
RT_ASSERT(model);
if (view == RT_NULL)
return;
if (model->length > 0)
{
struct rtgui_event_mv_model emodel;
RTGUI_EVENT_MV_MODEL_DELETED_INIT(&emodel);
emodel.first_data_changed_idx = 0;
emodel.last_data_changed_idx = model->length;
/* rtgui_mv_model_remove_view is to be called in thread context.
* Besides, it is called by _rtgui_mv_view_destructor which means the
* view will be invalid in the future. So we should call the event
* handler immediately. */
RTGUI_OBJECT(view)->event_handler(RTGUI_OBJECT(view), (struct rtgui_event *)&emodel);
}
_rtgui_mv_model_bare_remove(model, view);
_rtgui_mv_view_bare_remove(view, model);
}
RTM_EXPORT(rtgui_mv_model_remove_view);
rt_bool_t rtgui_mv_model_has_view(struct rtgui_mv_model *model, struct rtgui_mv_view *view)
{
RT_ASSERT(model);
if (view == RT_NULL || model->view_number == 0)
return RT_FALSE;
if (model->view_number == 1)
{
return model->view == view;
}
else
{
int i;
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
for (i = 0; i < model->view_number; i++)
{
if (view == view_arr[i])
return RT_TRUE;
}
return RT_FALSE;
}
}
RTM_EXPORT(rtgui_mv_model_has_view);
void rtgui_mv_model_set_data(struct rtgui_mv_model *model, rt_uint16_t dim, void *p)
{
RT_ASSERT(model);
RT_ASSERT(dim < model->dimension);
if (model->dimension == 1)
{
model->data = p;
return;
}
else
{
void **d_arr = (void **)model->data;
d_arr[dim] = p;
}
}
RTM_EXPORT(rtgui_mv_model_set_data);
void *rtgui_mv_model_get_data(struct rtgui_mv_model *model, rt_uint16_t dim)
{
RT_ASSERT(model);
RT_ASSERT(dim < model->dimension);
if (model->dimension == 1)
{
return model->data;
}
else
{
void **d_arr = (void **)model->data;
return d_arr[dim];
}
}
RTM_EXPORT(rtgui_mv_model_get_data);
static rt_bool_t _rtgui_mv_model_notify_view(struct rtgui_mv_model *model,
struct rtgui_mv_view *view,
struct rtgui_event_mv_model *emodel)
{
rt_thread_t target = RTGUI_WIDGET(view)->toplevel->app->tid;
emodel->model = model;
emodel->view = view;
return rtgui_send(target, (struct rtgui_event *)emodel, sizeof(*emodel));
}
void rtgui_mv_model_notify(struct rtgui_mv_model *model,
struct rtgui_event_mv_model *em)
{
/* model and view may not be in the same thread. Actually, model may not
* belong to any RTGUI thread. So we have to notify the views by sending
* events to the thread directly. */
if (model->view_number == 1)
{
struct rtgui_mv_view *view = (struct rtgui_mv_view *)model->view;
_rtgui_mv_model_notify_view(model, view, em);
}
else
{
int i;
struct rtgui_mv_view **view_arr = (struct rtgui_mv_view **)(model)->view;
for (i = 0; i < model->view_number; i++)
{
_rtgui_mv_model_notify_view(model, view_arr[i], em);
}
}
}
RTM_EXPORT(rtgui_mv_model_notify);