diff --git a/Makefile.am b/Makefile.am index dee94789e5..6278e421b9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -162,7 +162,8 @@ pc/ecore-cxx.pc \ pc/eolian-cxx.pc \ pc/edje-cxx.pc \ pc/eet-cxx.pc \ -pc/eo-cxx.pc +pc/eo-cxx.pc \ +pc/eio-cxx.pc endif if HAVE_ELUA diff --git a/configure.ac b/configure.ac index 9f4b269a6e..a8948f9a53 100644 --- a/configure.ac +++ b/configure.ac @@ -4573,6 +4573,7 @@ pc/ecore-avahi.pc pc/ector.pc pc/embryo.pc pc/eio.pc +pc/eio-cxx.pc pc/eldbus.pc pc/efreet.pc pc/efreet-mime.pc diff --git a/pc/.gitignore b/pc/.gitignore index 25049d8fa9..447b41789b 100644 --- a/pc/.gitignore +++ b/pc/.gitignore @@ -28,9 +28,12 @@ /eina-cxx.pc /eet-cxx.pc /eio.pc +/eio-cxx.pc /eldbus.pc /elocation.pc /embryo.pc +/emodel.pc +/emodel-cxx.pc /emotion.pc /eo.pc /ephysics.pc diff --git a/pc/eio-cxx.pc.in b/pc/eio-cxx.pc.in new file mode 100644 index 0000000000..82c6cfa84e --- /dev/null +++ b/pc/eio-cxx.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: eio C++ API +Description: Enlightenned Asynchronous Input Output library C++ API. +Requires.private: @requirements_pc_eio@ +Version: @VERSION@ +Libs: -L${libdir} -leio +Libs.private: @requirements_libs_eio@ +Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/eio-@VMAJ@ -I${includedir}/eio-cxx-@VMAJ@ diff --git a/pc/emodel-cxx.pc.in b/pc/emodel-cxx.pc.in new file mode 100644 index 0000000000..dce37642ad --- /dev/null +++ b/pc/emodel-cxx.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: emodel C++ API +Description: MVC Library C++ API. +Version: @PACKAGE_VERSION@ +Requires.private: @requirements_pc_emodel@ +Libs: -L${libdir} -lemodel +Libs.private: @requirements_libs_emodel@ +Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/emodel-@VMAJ@ -I${includedir}/emodel-cxx-@VMAJ@ diff --git a/pc/emodel.pc.in b/pc/emodel.pc.in new file mode 100644 index 0000000000..0d68f11152 --- /dev/null +++ b/pc/emodel.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: emodel +Description: MVC Library +Version: @PACKAGE_VERSION@ +Requires.private: @requirements_pc_emodel@ +Libs: -L${libdir} -lemodel +Libs.private: @requirements_libs_emodel@ +Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/emodel-@VMAJ@ diff --git a/pkgbuild/PKGBUILD b/pkgbuild/PKGBUILD index afa8ae2d7d..6a17c0b951 100644 --- a/pkgbuild/PKGBUILD +++ b/pkgbuild/PKGBUILD @@ -60,7 +60,7 @@ build() { package_efl() { provides+=("ecore=$pkgver" "eldbus=$pkgver" "edje=$pkgver" "eet=$pkgver" "eeze=$pkgver" "efreet=$pkgver" - "eina=$pkgver" "eio=$pkgver" "embryo=$pkgver" "emotion=$pkgver" + "eina=$pkgver" "eio=$pkgver" "embryo=$pkgver" "emotion=$pkgver" "emodel=$pkgver" "ephysics=$pkgver" "ethumb=$pkgver" "evas=$pkgver") conflicts+=('ecore' 'edje' 'eet' 'eeze' 'efreet' 'eina' 'eio' 'embryo' 'emotion' 'ethumb' 'evas') diff --git a/src/Makefile.am b/src/Makefile.am index 77403a7d87..5041799c65 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,8 @@ EOLIAN_FLAGS = -I$(srcdir)\ -I$(srcdir)/lib/efl/interfaces \ -I$(srcdir)/lib/ecore_audio \ -I$(srcdir)/lib/ecore \ - -I$(srcdir)/lib/ecore_con + -I$(srcdir)/lib/ecore_con \ + -I$(srcdir)/lib/eio DIST_SUBDIRS = SUBDIRS = @@ -77,6 +78,7 @@ include Makefile_Eo_Cxx.am include Makefile_Efl_Cxx.am include Makefile_Edje_Cxx.am include Makefile_Evas_Cxx.am +include Makefile_Eio_Cxx.am include Makefile_Elua.am include Makefile_Elocation.am diff --git a/src/Makefile_Efl.am b/src/Makefile_Efl.am index b65dafc406..5a14d7e028 100644 --- a/src/Makefile_Efl.am +++ b/src/Makefile_Efl.am @@ -12,7 +12,8 @@ efl_eolian_files = \ lib/efl/interfaces/efl_gfx_shape.eo \ lib/efl/interfaces/efl_gfx_gradient_base.eo \ lib/efl/interfaces/efl_gfx_gradient_linear.eo \ - lib/efl/interfaces/efl_gfx_gradient_radial.eo + lib/efl/interfaces/efl_gfx_gradient_radial.eo \ + lib/efl/interfaces/efl_model_base.eo efl_eolian_files_h = $(efl_eolian_files:%.eo=%.eo.h) efl_eolian_files_c = $(efl_eolian_files:%.eo=%.eo.c) @@ -28,6 +29,7 @@ CLEANFILES += \ EXTRA_DIST += \ lib/efl/Efl_Config.h \ lib/efl/Efl.h \ + lib/efl/Efl_Model_Common.h \ $(efl_eolian_files) efleolianfilesdir = $(datadir)/eolian/include/efl-@VMAJ@ diff --git a/src/Makefile_Efl_Cxx.am b/src/Makefile_Efl_Cxx.am index fbcc5632ca..9df7c60bdd 100644 --- a/src/Makefile_Efl_Cxx.am +++ b/src/Makefile_Efl_Cxx.am @@ -19,7 +19,7 @@ generated_efl_cxx_bindings = \ lib/efl/interfaces/efl_gfx_gradient_radial.eo.hh lib/efl/Efl.hh: $(generated_efl_cxx_bindings) - @echo @ECHO_E@ "#ifndef EFL_CXX_EDJE_HH\n#define EFL_CXX_EDJE_HH\n" > $(top_builddir)/src/lib/efl/Efl.hh + @echo @ECHO_E@ "#ifndef EFL_CXX_HH\n#define EFL_CXX_HH\n" > $(top_builddir)/src/lib/efl/Efl.hh @echo @ECHO_E@ "#ifdef EFL_BETA_API_SUPPORT" >> $(top_builddir)/src/lib/efl/Efl.hh @for i in $(generated_efl_cxx_bindings); do echo "#include <$$(basename $$i)>" >> $(top_builddir)/src/lib/efl/Efl.hh; done @echo @ECHO_E@ "#endif\n\n#endif\n" >> $(top_builddir)/src/lib/efl/Efl.hh diff --git a/src/Makefile_Eio.am b/src/Makefile_Eio.am index 47f746f2c8..03f1d1389a 100644 --- a/src/Makefile_Eio.am +++ b/src/Makefile_Eio.am @@ -1,10 +1,24 @@ ### Library +EIO_EOS = \ + lib/eio/eio_model.eo + +EIO_EOS_H = $(EIO_EOS:%.eo=%.eo.h) +EIO_EOS_C = $(EIO_EOS:%.eo=%.eo.c) + +BUILT_SOURCES += $(EIO_EOS_C) $(EIO_EOS_H) + +eioeolianfilesdir = $(datadir)/eolian/include/eio-@VMAJ@ +eioeolianfiles_DATA = $(EIO_EOS) + lib_LTLIBRARIES += lib/eio/libeio.la +EXTRA_DIST += $(eioeolianfiles_DATA) installed_eiomainheadersdir = $(includedir)/eio-@VMAJ@ + dist_installed_eiomainheaders_DATA = lib/eio/Eio.h lib/eio/eio_inline_helper.x +nodist_installed_eiomainheaders_DATA = $(EIO_EOS_H) lib_eio_libeio_la_SOURCES = \ lib/eio/eio_dir.c \ @@ -16,6 +30,8 @@ lib/eio/eio_monitor.c \ lib/eio/eio_monitor_poll.c \ lib/eio/eio_single.c \ lib/eio/eio_xattr.c \ +lib/eio/eio_model.c \ +lib/eio/eio_model_private.h \ lib/eio/eio_private.h if HAVE_INOTIFY @@ -30,9 +46,9 @@ endif endif endif -lib_eio_libeio_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EIO_CFLAGS@ -lib_eio_libeio_la_LIBADD = @EIO_LIBS@ -lib_eio_libeio_la_DEPENDENCIES = @EIO_INTERNAL_LIBS@ +lib_eio_libeio_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @EIO_CFLAGS@ @EFL_CFLAGS@ +lib_eio_libeio_la_LIBADD = @EIO_LIBS@ @EFL_LIBS@ +lib_eio_libeio_la_DEPENDENCIES = @EIO_INTERNAL_LIBS@ @EFL_INTERNAL_LIBS@ lib_eio_libeio_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ if HAVE_NOTIFY_COCOA lib_eio_libeio_la_LDFLAGS += -framework CoreServices @@ -45,6 +61,8 @@ TESTS += tests/eio/eio_suite tests_eio_eio_suite_SOURCES = \ tests/eio/eio_suite.c \ +tests/eio/eio_model_test_file.c \ +tests/eio/eio_model_test_monitor_add.c \ tests/eio/eio_test_monitor.c tests_eio_eio_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ @@ -52,16 +70,20 @@ tests_eio_eio_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -DTESTS_BUILD_DIR=\"$(top_builddir)/src/tests/eio\" \ @CHECK_CFLAGS@ \ @ECORE_FILE_CFLAGS@ \ -@EIO_CFLAGS@ +@EIO_CFLAGS@ @EFL_CFLAGS@ tests_eio_eio_suite_LDADD = \ @CHECK_LIBS@ \ @USE_ECORE_FILE_LIBS@ \ @USE_EIO_LIBS@ +@USE_EFL_LIBS@ tests_eio_eio_suite_DEPENDENCIES = \ @USE_EIO_INTERNAL_LIBS@ endif -EXTRA_DIST += tests/eio/eio_suite.h +EXTRA_DIST += \ +tests/eio/eio_suite.h \ +lib/eio/eio_model.h + diff --git a/src/Makefile_Eio_Cxx.am b/src/Makefile_Eio_Cxx.am new file mode 100644 index 0000000000..3f5237169c --- /dev/null +++ b/src/Makefile_Eio_Cxx.am @@ -0,0 +1,22 @@ +if HAVE_CXX11 + +### Generated headers + +generated_eio_cxx_bindings = lib/eio/eio_model.eo.hh + +lib/eio/Eio.hh: $(generated_eio_cxx_bindings) + @echo @ECHO_E@ "#ifndef EFL_CXX_EIO_HH\n#define EFL_CXX_EIO_HH\n" > $(top_builddir)/src/lib/eio/Eio.hh + @echo @ECHO_E@ "#ifdef EFL_BETA_API_SUPPORT" >> $(top_builddir)/src/lib/eio/Eio.hh + @for i in $(generated_eio_cxx_bindings); do echo "#include <$$(basename $$i)>" >> $(top_builddir)/src/lib/eio/Eio.hh; done + @echo @ECHO_E@ "#endif\n\n#endif\n" >> $(top_builddir)/src/lib/eio/Eio.hh + +generated_eio_cxx_all = \ + $(generated_eio_cxx_bindings) \ + lib/eio/Eio.hh + +CLEANFILES += $(generated_eio_cxx_all) + +installed_eiocxxmainheadersdir = $(includedir)/eio-cxx-@VMAJ@/ +nodist_installed_eiocxxmainheaders_DATA = $(generated_eio_cxx_all) + +endif diff --git a/src/examples/eio/Makefile.am b/src/examples/eio/Makefile.am index a424f77084..c80f74739a 100644 --- a/src/examples/eio/Makefile.am +++ b/src/examples/eio/Makefile.am @@ -22,6 +22,7 @@ eio_file_copy eio_file_ls_SOURCES = eio_file_ls.c eio_file_ls_LDADD = \ $(top_builddir)/src/lib/eio/libeio.la \ +$(top_builddir)/src/lib/emodel/libemodel.la \ $(top_builddir)/src/lib/eo/libeo.la \ $(top_builddir)/src/lib/ecore/libecore.la \ $(top_builddir)/src/lib/eet/libeet.la \ @@ -32,6 +33,7 @@ $(top_builddir)/src/lib/eina/libeina.la \ eio_file_copy_SOURCES = eio_file_copy.c eio_file_copy_LDADD = \ $(top_builddir)/src/lib/eio/libeio.la \ +$(top_builddir)/src/lib/emodel/libemodel.la \ $(top_builddir)/src/lib/eo/libeo.la \ $(top_builddir)/src/lib/ecore/libecore.la \ $(top_builddir)/src/lib/eet/libeet.la \ diff --git a/src/lib/efl/Efl.h b/src/lib/efl/Efl.h index 5a0b3eb949..2de76807d2 100644 --- a/src/lib/efl/Efl.h +++ b/src/lib/efl/Efl.h @@ -139,6 +139,8 @@ typedef enum _Efl_Gfx_Fill_Spread #ifdef EFL_BETA_API_SUPPORT +#include + /* Interfaces */ #include "interfaces/efl_control.eo.h" #include "interfaces/efl_file.eo.h" @@ -146,6 +148,7 @@ typedef enum _Efl_Gfx_Fill_Spread #include "interfaces/efl_player.eo.h" #include "interfaces/efl_text.eo.h" #include "interfaces/efl_text_properties.eo.h" +#include "interfaces/efl_model_base.eo.h" EAPI extern const Eo_Event_Description _EFL_GFX_CHANGED; EAPI extern const Eo_Event_Description _EFL_GFX_PATH_CHANGED; diff --git a/src/lib/efl/Efl_Model_Common.h b/src/lib/efl/Efl_Model_Common.h new file mode 100644 index 0000000000..bd9ab62e5f --- /dev/null +++ b/src/lib/efl/Efl_Model_Common.h @@ -0,0 +1,92 @@ +#ifndef _EFL_MODEL_COMMON_H +#define _EFL_MODEL_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @enum _Efl_Model_Load_Status + * XXX/TODO/FIXME: Remove this enum (and possibly other data) from here + * as soon as eolian translates these data types in .eo's. + */ +enum _Efl_Model_Load_Status + { + EFL_MODEL_LOAD_STATUS_ERROR = 0, + EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES = (1 << 0), + EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN = (1 << 1), + EFL_MODEL_LOAD_STATUS_LOADING = (1 << 0) | (1 << 1), + + EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES = (1 << 2), + EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN = (1 << 3), + EFL_MODEL_LOAD_STATUS_LOADED = (1 << 2) | (1 << 3), + + EFL_MODEL_LOAD_STATUS_UNLOADING = (1 << 4), + EFL_MODEL_LOAD_STATUS_UNLOADED = (1 << 5) + }; +/** + * @typedef Efl_Model_Load_Status + */ +typedef enum _Efl_Model_Load_Status Efl_Model_Load_Status; + +/** + * @struct _Efl_Model_Load + * Structure to hold Efl_Model_Load_Status enum + * (and possible other data) to avoid ABI break. + */ +struct _Efl_Model_Load + { + Efl_Model_Load_Status status; + /* add more data below here if necessary */ + }; + +/** + * @typedef Efl_Model_Load + */ +typedef struct _Efl_Model_Load Efl_Model_Load; + +/** + * @struct _Efl_Model_Property_Event + */ +struct _Efl_Model_Property_Event +{ + const Eina_Array *changed_properties; /**< array of property name */ + const Eina_Array *invalidated_properties; /**< array of property name */ +}; + +/** + * @typedef Efl_Model_Property_Event + */ +typedef struct _Efl_Model_Property_Event Efl_Model_Property_Event; + +/** + * @struct _Efl_Model_Children_Event + * Every time a child id added the event + * EFL_MODEL_EVENT_CHILD_ADDED is dispatched + * passing along this structure. + */ +struct _Efl_Model_Children_Event +{ + Eo *child; /**< child, for child_add */ + /** + * index is a hint and is intended + * to provide a way for applications + * to control/know children relative + * positions through listings. + * + * NOTE: If listing is performed asynchronously + * exact order may not be guaranteed. + */ + unsigned int index; +}; + +/** + * @struct Efl_Model_Children_Event + */ +typedef struct _Efl_Model_Children_Event Efl_Model_Children_Event; + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/lib/efl/interfaces/efl_interfaces_main.c b/src/lib/efl/interfaces/efl_interfaces_main.c index 6ae1cb3ea4..31a3aae616 100644 --- a/src/lib/efl/interfaces/efl_interfaces_main.c +++ b/src/lib/efl/interfaces/efl_interfaces_main.c @@ -25,3 +25,5 @@ EAPI const Eo_Event_Description _EFL_GFX_CHANGED = EAPI const Eo_Event_Description _EFL_GFX_PATH_CHANGED = EO_EVENT_DESCRIPTION("Graphics path changed", "The path of a shape object changed"); + +#include "interfaces/efl_model_base.eo.c" diff --git a/src/lib/efl/interfaces/efl_model_base.eo b/src/lib/efl/interfaces/efl_model_base.eo new file mode 100644 index 0000000000..75e280ebd4 --- /dev/null +++ b/src/lib/efl/interfaces/efl_model_base.eo @@ -0,0 +1,322 @@ +/* + * type Efl_Model_Load_Status: enum _Efl_Model_Load_Status + * { + * EFL_MODEL_LOAD_STATUS_ERROR = 0, /*@ Error in Load Model * + * EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES = (1 << 0), /*@ properties load in progress * + * EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN = (1 << 1), /*@ children load in progress * + * EFL_MODEL_LOAD_STATUS_LOADING = (1 << 0) | (1 << 1), /*@ children and properties load in progress * + * + * EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES = (1 << 2), /*@ Model as ready to fetch properties * + * EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN = (1 << 3), /*@ Model as ready to fetch children * + * EFL_MODEL_LOAD_STATUS_LOADED = (1 << 2) | (1 << 3), /*@ Model as ready to fetch properties and children * + * + * EFL_MODEL_LOAD_STATUS_UNLOADING = (1 << 4), /*@ Model Unload in progress * + * EFL_MODEL_LOAD_STATUS_UNLOADED = (1 << 5) /*@ Model Unloaded * + * } + * + * + * type Efl_Model_Property_Event: struct _Efl_Model_Property_Event + * { + * changed_properties: Eina_Array* ; /*@ List of changed properties * + * invalidated_properties: Eina_Array* ; /*@ Removed properties identified by name * + * } + */ + +interface Efl.Model.Base () +{ + legacy_prefix: null; + eo_prefix: efl_model; + properties { + load_status { + get { + /*@ + Get a load emodel current status. + + @return: @c Efl_Model_Load_Status + + By convention this means get the current model status. + Possible values are defined Efl_Model_Load_Status enumerator. + + @see Efl_Model_Load_Status + @see efl_model_load + + @since 1.14 */ + return: Efl_Model_Load_Status; + } + } + properties { + get { + /*@ + Get properties from model. + + @return: @c Efl_Model_Load_Status + + properties_get is due to provide callers a way the fetch the current + properties implemented/used by the model. + The event EFL_MODEL_EVENT_PROPERTIES_CHANGE will be raised to notify listeners + of any modifications in the properties. + + @see EFL_MODEL_EVENT_PROPERTIES_CHANGE + @since 1.14 */ + + return: Efl_Model_Load_Status; + } + values { + const(array*) properties; /*@ array of current properties */ + } + } + property { + set { + /*@ + Set a property value of a given property name. + + @return: @c EINA_TRUE, on success, @c EINA_FALSE in readonly property or error + + The caller must ensure to call at least efl_model_prop_list before being + able to see/set properties. + This function sets a new property value into given property name. Once + the operation is completed the concrete implementation should raise + EFL_MODEL_EVENT_PROPERTIES_CHANGE event in order to notify listeners of the + new value of the property. + + If the model doesn't have the property then there are two possibilities, + either raise an error or create the new property in model + + @see efl_model_property_get + @see EFL_MODEL_EVENT_PROPERTIES_CHANGE + @since 1.14 */ + + return: Efl_Model_Load_Status; + } + get { + /*@ + Retrieve the value of a given property name. + + @return: @c Load Status, on success, @c EFL_MODEL_LOAD_STATUS_ERROR otherwise + + property_get will only be available when load status is equal to + EFL_MODEL_LOAD_STATUS_LOADED. + + At this point the caller is free to get values from properties. + The event EFL_MODEL_EVENT_PROPERTIES_CHANGE may be raised to notify + listeners of the property/value. + + @see efl_model_properties_get + @see EFL_MODEL_EVENT_PROPERTIES_CHANGE + + @since 1.14 */ + return: Efl_Model_Load_Status; + } + keys { + const(char)* property; /*@ Property name */ + } + values { + const(Eina_Value)* value; /*@ New value */ + } + } + children_slice { + get { + /*@ + Get children slice OR full range. + + @return: @c Efl_Model_Load_Status. See below for more info. + + Before being able to get the children list the model status must be + on loaded status (EFL_MODEL_LOAD_STATUS_LOADED). + However there may be circunstancies where the model could be + in a different state, in such cases it is advisable + to simply return: its current state, which will be + of course, different than @c EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN. + When children accessor is return:ed as NULL one should then + test the current load status return:ed by @children_slice_get + in order to check against an empty list or real error. + + children_slice_get behaves in two different ways, it may provide + the slice if both @c start AND @c count are non-zero OR full range otherwise. + + The return:ed Eina_Accessor must be freed when it is no longer needed and + eo_unref() must be invoked for children if caller wants a copy. + + Since 'slice' is a range, for example if we have 20 childs a slice could be + the range from 3(start) to 4(count), see: + child 0 [no] + child 1 [no] + child 2 [yes] + child 3 [yes] + child 4 [yes] + child 5 [yes] + child 6 [no] + child 7 [no] + + Optionally the user can call children_count_get to know + the number of children so a valid range can be known in advance. + + Below are examples of both usage types: slices and full ranges. + @code + + // Returns full list + eo_do(obj, efl_model_children_slice_get(0, 0, &children_accessor)); + + // Returns 5 items, counting from item #5 + eo_do(obj, efl_model_children_slice_get(5, 5, &children_accessor)); + + @endcode + + @see efl_model_children_get + @see efl_model_children_count_get + @see efl_model_load + @see efl_model_load_status_get + @since 1.14 */ + + return: Efl_Model_Load_Status; + } + keys { + unsigned start; /*@ Range begin - start from here. If start and count are 0 slice is ignored.*/ + unsigned count; /*@ Range size. If count and start are 0 slice is ignored.*/ + } + values { + accessor* children_accessor; + } + } + children_count { + get { + /*@ + Get children count. + + @return: @c EINA_TRUE, on success, @c EINA_FALSE otherwise + + When efl_model_load is completed efl_model_coildren_count_get can be use + to get the number of children. children_count_get can also be used + before calling children_slice_get so a valid range is known. + Event EFL_MODEL_CHILDREN_COUNT_CHANGED is emitted when count is finished. + + @see efl_model_children_get + @see efl_model_children_slice_get + @see efl_model_load + @see efl_model_load_status_get + @since 1.14 */ + + return: Efl_Model_Load_Status; + } + values { + unsigned children_count; + } + } + } + methods { + load { + /*@ + Load emodel. + + By convention this means loading data from an external source and populating + the models properties and children with it. For example in the case of file + system backed model, this means opening the relevant files and reading the + data from them(creating the properties and children from it). + the model emit EFL_MODEL_EVENT_LOAD_STATUS after end with Efl_Model_Load_Status + @warning This convention should be followed, but no guarantees of behaviour + by user defined types can be given. + + Alternatively is possible to use properties_load to load only properties + and children_load to load only children. If efl_model_load is called then + calling properties_load and/or children_load is not necessary. + + @see Efl_Model_Load_Status + @see efl_model_properties_load + @see efl_model_children_load + @see efl_model_unload + @see efl_model_load_status_get + + @since 1.14 */ + } + unload { + /*@ + Unload emodel. + + By convention this means releasing data received/read from an external source. For + example of a database backed model this might mean releasing the iterator for + the currently loaded data or deleting a temporary table. + the model emit EFL_MODEL_EVENT_LOAD_STATUS after end with model load status + @warning This convention should be followed, but no guarantees of behaviour + by user defined types can be given. + + @see Efl_Model_Load_Status + @see efl_model_load + @see efl_model_load_status_get + + @since 1.14 */ + } + properties_load { + /*@ + Properties emodel load. + + By convention this means loading data from an external source and populating + the models properties only. This method is a subset of efl_model_load, meaning that + it won't load children, it is a hint. + For loadind both properties and children use efl_model_load + instead. + + @see efl_model_load + + @since 1.14 */ + } + children_load { + /*@ + Children emodel load. + + By convention this means loading data from an external source and populating + the models children only. This method is a subset of efl_model_load, meaning that + it won't load properties. For loadind both properties and children use efl_model_load + instead. + + @see efl_model_load + + @since 1.14 */ + } + child_add { + /*@ + Add a new child. + + @return: @c Emodel* on success, @c NULL otherwise + + Add a new child, possibly dummy, depending on the implementation, + of a internal keeping. When the child is effectively + added the event EFL_MODEL_EVENT_CHILD_ADD is then raised and the new child + is kept along with other children. + + @see EFL_MODEL_EVENT_CHILD_ADD + @see load_status_get + + @since 1.14 */ + + return: Eo *; + } + child_del { + /*@ + Remove a child. + + @return: @c Efl_Model_Load_Status on success, @c EFL_MODEL_LOAD_STATUS_ERROR otherwise. + + Remove a child of a internal keeping. When the child is effectively + removed the event EFL_MODEL_EVENT_CHILD_REMOVED is then raised to give a + chance for listeners to perform any cleanup and/or update references. + + @see EFL_MODEL_EVENT_CHILD_REMOVED + @since 1.14 */ + + return: Efl_Model_Load_Status; + + params { + @in Eo* child; /*@ Child to be removed */ + } + } + } + + events { + load,status: Efl_Model_Load_Status; /*@ Event dispatch when load status changes */ + properties,changed: Efl_Model_Properties_Evt; /*@ Event dispatched when properties list is available. */ + child,added; /*@ Event dispatched when new child is added. */ + child,removed; /*@ Event dispatched when child is removed. */ + children,count,changed; /*@ Event dispatched when children count is finished. */ + } +} + diff --git a/src/lib/eio/eio_model.c b/src/lib/eio/eio_model.c new file mode 100644 index 0000000000..0a93658331 --- /dev/null +++ b/src/lib/eio/eio_model.c @@ -0,0 +1,683 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include "eio_private.h" +#include "eio_model_private.h" + +#define MY_CLASS EIO_MODEL_CLASS +#define MY_CLASS_NAME "Eio_Model" + +static void _eio_prop_set_error_cb(void *, Eio_File *, int); +static void _eio_model_efl_model_base_properties_load(Eo *, Eio_Model_Data *); +static void _eio_model_efl_model_base_children_load(Eo *, Eio_Model_Data *); + +static void +_load_set(Eio_Model_Data *priv, Efl_Model_Load_Status status) +{ + Efl_Model_Load load; + + load.status = status; + if ((priv->load.status & (EFL_MODEL_LOAD_STATUS_LOADED | EFL_MODEL_LOAD_STATUS_LOADING)) && + (load.status & (EFL_MODEL_LOAD_STATUS_LOADED | EFL_MODEL_LOAD_STATUS_LOADING))) + { + load.status = priv->load.status | status; + switch (status) + { + case EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES: + load.status &= ~EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES; + break; + case EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES: + load.status &= ~EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES; + break; + case EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN: + load.status &= ~EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN; + break; + case EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN: + load.status &= ~EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN; + break; + default: break; + } + } + + if (priv->load.status != load.status) + { + priv->load.status = load.status; + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_LOAD_STATUS, &load)); + } +} + +/** + * Callbacks + * Property + */ +static void +_eio_stat_done_cb(void *data, Eio_File *handler EINA_UNUSED, const Eina_Stat *stat) +{ + Efl_Model_Property_Event evt; + Eio_Model_Data *priv = data; + EINA_SAFETY_ON_FALSE_RETURN(eo_ref_get(priv->obj)); + + priv->is_dir = eio_file_is_dir(stat); + memset(&evt, 0, sizeof(Efl_Model_Property_Event)); + + eina_value_set(priv->properties_value[EIO_MODEL_PROP_IS_DIR], eio_file_is_dir(stat)); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_IS_LNK], eio_file_is_lnk(stat)); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_MTIME], eio_file_mtime(stat)); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_SIZE], eio_file_size(stat)); + + evt.changed_properties = priv->properties_name; + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt)); + + _load_set(priv, EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES); + + if (priv->load_pending & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + _eio_model_efl_model_base_children_load(priv->obj, priv); +} + +static void +_eio_progress_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, const Eio_Progress *info EINA_UNUSED) +{ + //TODO: implement +} + +static void +_eio_move_done_cb(void *data, Eio_File *handler EINA_UNUSED) +{ + Efl_Model_Property_Event evt; + Eio_Model_Data *priv = data; + Eina_Array *properties; + + EINA_SAFETY_ON_FALSE_RETURN(eo_ref_get(priv->obj)); + + memset(&evt, 0, sizeof(Efl_Model_Property_Event)); + + /** + * When mv is executed we update our values and + * notify both path and filename properties listeners. + */ + eina_value_set(priv->properties_value[EIO_MODEL_PROP_PATH], priv->path); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_FILENAME], basename(priv->path)); + + properties = eina_array_new(2); + eina_array_push(properties, _eio_model_prop_names[EIO_MODEL_PROP_PATH]); + eina_array_push(properties, _eio_model_prop_names[EIO_MODEL_PROP_FILENAME]); + evt.changed_properties = properties; + + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, &evt)); + eina_array_free(properties); +} + +static void +_eio_error_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, int error) +{ + if (error != 0) + { + ERR("%d: %s.", error, strerror(error)); + } +} + +static void +_eio_prop_set_error_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, int error) +{ + if (error != 0) + { + ERR("%d: %s.", error, strerror(error)); + } +} + + +/** + * Callbacks + * Ecore Events + */ +static Eina_Bool +_efl_model_evt_added_ecore_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED) +{ + Eio_Monitor_Event *evt = (Eio_Monitor_Event*)event; + Eio_Model_Data *priv = data; + Efl_Model_Children_Event cevt; + Eina_Value path; + + if (priv->children_list) + { + cevt.child = eo_add_ref(EIO_MODEL_CLASS, priv->obj, eio_model_path_set(evt->filename)); + priv->children_list = eina_list_append(priv->children_list, cevt.child); + cevt.index = eina_list_count(priv->children_list); + + eina_value_setup(&path, EINA_VALUE_TYPE_STRING); + eina_value_set(&path, evt->filename); + eo_do(cevt.child, eio_model_children_filter_set(priv->filter_cb, priv->filter_userdata)); + eina_value_flush(&path); + + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILD_ADDED, &cevt)); + } + + return EINA_TRUE; +} + +static Eina_Bool +_efl_model_evt_deleted_ecore_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED) +{ + Eio_Monitor_Event *evt = (Eio_Monitor_Event*)event; + Eio_Model_Data *priv = data; + + if (priv->children_list) + { + Eina_List* cur = priv->children_list; + int i; + + for (i = 0; cur; ++i, cur = cur->next) + { + Eio_Model_Data *cur_priv = eo_data_scope_get(cur->data, MY_CLASS); + if(strcmp(cur_priv->path, evt->filename) == 0) + break; + } + + if (cur) + { + Efl_Model_Children_Event cevt; + cevt.index = i; + cevt.child = cur->data; + + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILD_REMOVED, &cevt)); + + priv->children_list = eina_list_remove_list(priv->children_list, cur); + eo_unref(cevt.child); + } + } + + return EINA_TRUE; +} + +static void +_eio_monitors_list_load(Eio_Model_Data *priv) +{ + priv->mon.mon_event_child_add[0] = EIO_MONITOR_DIRECTORY_CREATED; + priv->mon.mon_event_child_add[1] = EIO_MONITOR_FILE_CREATED; + priv->mon.mon_event_child_add[2] = EIO_MONITOR_ERROR; + priv->mon.mon_event_child_del[0] = EIO_MONITOR_DIRECTORY_DELETED; + priv->mon.mon_event_child_del[1] = EIO_MONITOR_FILE_DELETED; + priv->mon.mon_event_child_del[2] = EIO_MONITOR_ERROR; +} + +/** + * Callbacks + * Child Del + */ +static Eina_Bool +_eio_filter_child_del_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, const Eina_File_Direct_Info *info EINA_UNUSED) +{ + return EINA_TRUE; +} + +static void +_eio_progress_child_del_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, const Eio_Progress *info EINA_UNUSED) +{} + +static void +_eio_done_unlink_cb(void *data, Eio_File *handler EINA_UNUSED) +{ + Eio_Model_Data *priv = data; + + EINA_SAFETY_ON_NULL_RETURN(priv); + EINA_SAFETY_ON_NULL_RETURN(priv->obj); + + eo_unref(priv->obj); +} + +static void +_eio_error_unlink_cb(void *data EINA_UNUSED, Eio_File *handler EINA_UNUSED, int error) +{ + Eio_Model_Data *priv = data; + + ERR("%d: %s.", error, strerror(error)); + + eo_unref(priv->obj); +} + + +/** + * Interfaces impl. + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_properties_get(Eo *obj EINA_UNUSED, + Eio_Model_Data *_pd, Eina_Array * const* properties) +{ + Eio_Model_Data *priv = _pd; + + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(priv->obj, EINA_FALSE); + + *(Eina_Array **)properties = priv->properties_name; + + return priv->load.status; +} + +/** + * Property Get + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_property_get(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, const char *property, const Eina_Value **value) +{ + unsigned int i; + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EFL_MODEL_LOAD_STATUS_ERROR); + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, EFL_MODEL_LOAD_STATUS_ERROR); + + *value = NULL; + if (priv->load.status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + { + for (i = 0; i < EIO_MODEL_PROP_LAST; ++i) + { + if (!strcmp(property, _eio_model_prop_names[i])) + break; + } + + if ( i < EIO_MODEL_PROP_LAST) + { + *value = priv->properties_value[i]; + } + } + + return priv->load.status; +} + +/** + * Property Set + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_property_set(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, const char * property, const Eina_Value *value) +{ + char *dest; + + EINA_SAFETY_ON_NULL_RETURN_VAL(property, EINA_FALSE); + + if (strcmp(property, "path") != 0) + return EINA_FALSE; + + dest = eina_value_to_string(value); + if (priv->path == NULL) + { + priv->path = dest; + + INF("path '%s' with filename '%s'.", priv->path, basename(priv->path)); + + eina_value_set(priv->properties_value[EIO_MODEL_PROP_PATH], priv->path); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_FILENAME], basename(priv->path)); + + _eio_monitors_list_load(priv); + + _eio_move_done_cb(priv, NULL); + + if (priv->load_pending & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + _eio_model_efl_model_base_properties_load(obj, priv); + else if (priv->load_pending & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + _eio_model_efl_model_base_children_load(obj, priv); + + return priv->load.status; + } + + priv->file = eio_file_move(priv->path, dest, _eio_progress_cb, _eio_move_done_cb, _eio_prop_set_error_cb, priv); + free(priv->path); + priv->path = dest; + + return priv->load.status; +} +/** + * Children Count Get + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_children_count_get(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, unsigned int *children_count) +{ + /**< eina_list_count returns 'unsigned int' */ + *children_count = eina_list_count(priv->children_list); + return priv->load.status; +} + +/** + * Properties Load + */ +static void +_eio_model_efl_model_base_properties_load(Eo *obj EINA_UNUSED, Eio_Model_Data *priv) +{ + if (priv->path == NULL) + { + priv->load_pending |= EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES; + return; + } + priv->load_pending &= ~EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES; + + if (!(priv->load.status & (EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES | EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES))) + { + _load_set(priv, EFL_MODEL_LOAD_STATUS_LOADING_PROPERTIES); + priv->file = eio_file_direct_stat(priv->path, _eio_stat_done_cb, _eio_error_cb, priv); + } +} + +static void +_eio_model_efl_model_base_monitor_add(Eio_Model_Data *priv) +{ + if (!priv->monitor) + { + priv->monitor = eio_monitor_add(priv->path); + int i = 0; + + for (i = 0; priv->mon.mon_event_child_add[i] != EIO_MONITOR_ERROR ; ++i) + priv->mon.ecore_child_add_handler[i] = + ecore_event_handler_add(priv->mon.mon_event_child_add[i], _efl_model_evt_added_ecore_cb, priv); + + for (i = 0; priv->mon.mon_event_child_del[i] != EIO_MONITOR_ERROR ; ++i) + priv->mon.ecore_child_add_handler[i] = + ecore_event_handler_add(priv->mon.mon_event_child_del[i], _efl_model_evt_deleted_ecore_cb, priv); + } +} + +/* + * Callbacks + * Children Load + */ +static Eina_Bool +_eio_filter_children_load_cb(void *data, Eio_File *handler, const Eina_File_Direct_Info *info) +{ + Eio_Model_Data *priv = data; + EINA_SAFETY_ON_NULL_RETURN_VAL(priv, EINA_FALSE); + + if (priv->filter_cb) + { + return priv->filter_cb(priv->filter_userdata, handler, info); + } + + return EINA_TRUE; +} + +static void +_eio_main_children_load_cb(void *data, Eio_File *handler EINA_UNUSED, const Eina_File_Direct_Info *info) +{ + Eo *child; + Eio_Model_Data *priv = data; + EINA_SAFETY_ON_NULL_RETURN(priv); + + child = eo_add(MY_CLASS, NULL, eio_model_path_set(info->path)); + if (priv->filter_cb) + eo_do(child, eio_model_children_filter_set(priv->filter_cb, priv->filter_userdata)); + + priv->children_list = eina_list_append(priv->children_list, child); +} + +static void +_eio_done_children_load_cb(void *data, Eio_File *handler EINA_UNUSED) +{ + unsigned long count; + Eio_Model_Data *priv = data; + EINA_SAFETY_ON_NULL_RETURN(priv); + + count = eina_list_count(priv->children_list); + _load_set(priv, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); + + eo_do(priv->obj, eo_event_callback_call(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, &count)); +} + +static void +_eio_error_children_load_cb(void *data, Eio_File *handler EINA_UNUSED, int error) +{ + Eio_Model_Data *priv = data; + Eo *child; + + ERR("%d: %s.", error, strerror(error)); + + EINA_LIST_FREE(priv->children_list, child) + eo_unref(child); + + _load_set(priv, EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN); +} + +/** + * Children Load + */ +static void +_eio_model_efl_model_base_children_load(Eo *obj EINA_UNUSED, Eio_Model_Data *priv) +{ + if (priv->path == NULL) + { + priv->load_pending |= EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN; + return; + } + + priv->load_pending &= ~EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN; + + if (priv->children_list == NULL && priv->is_dir && + !(priv->load.status & (EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN | EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN))) + { + _eio_model_efl_model_base_monitor_add(priv); + + _load_set(priv, EFL_MODEL_LOAD_STATUS_LOADING_CHILDREN); + eio_file_direct_ls(priv->path, _eio_filter_children_load_cb, + _eio_main_children_load_cb, _eio_done_children_load_cb, + _eio_error_children_load_cb, priv); + } +} + +/** + * Load + */ +static void +_eio_model_efl_model_base_load(Eo *obj, Eio_Model_Data *priv) +{ + priv->load_pending |= EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN; + _eio_model_efl_model_base_properties_load(obj, priv); +} + +/** + * Load status get + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_load_status_get(Eo *obj EINA_UNUSED, Eio_Model_Data *priv) +{ + return priv->load.status; +} + +/** + * Unload + */ +static void +_eio_model_efl_model_base_unload(Eo *obj EINA_UNUSED, Eio_Model_Data *priv) +{ + if (!(priv->load.status & EFL_MODEL_LOAD_STATUS_UNLOADED)) + { + Eo *child; + EINA_LIST_FREE(priv->children_list, child) + { + eo_unref(child); + } + + _load_set(priv, EFL_MODEL_LOAD_STATUS_UNLOADED); + } +} + +static void +_eio_model_children_filter_set(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, Eio_Filter_Direct_Cb filter_cb, void *data) +{ + priv->filter_cb = filter_cb; + priv->filter_userdata = data; +} + +/** + * Child Add + */ +static Eo * +_eio_model_efl_model_base_child_add(Eo *obj EINA_UNUSED, Eio_Model_Data *priv EINA_UNUSED) +{ + return eo_add(EIO_MODEL_CLASS, obj); +} + +static void +_eio_model_efl_model_base_child_del_stat(void* data, Eio_File* handler EINA_UNUSED, const Eina_Stat* stat) +{ + Eo* child = data; + Eio_Model_Data *child_priv = eo_data_scope_get(child, MY_CLASS); + + if(eio_file_is_dir(stat)) + eio_dir_unlink(child_priv->path, + _eio_filter_child_del_cb, + _eio_progress_child_del_cb, + _eio_done_unlink_cb, + _eio_error_unlink_cb, + child_priv); + else + eio_file_unlink(child_priv->path, _eio_done_unlink_cb, _eio_error_unlink_cb, child_priv); +} + +/** + * Child Remove + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_child_del(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, Eo *child) +{ + Eio_Model_Data *child_priv; + EINA_SAFETY_ON_NULL_RETURN_VAL(child, EFL_MODEL_LOAD_STATUS_ERROR); + + if (priv->children_list != NULL) + { + priv->children_list = eina_list_remove(priv->children_list, child); + } + + child_priv = eo_data_scope_get(child, MY_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_priv, EFL_MODEL_LOAD_STATUS_ERROR); + + eio_file_direct_stat(child_priv->path, + &_eio_model_efl_model_base_child_del_stat, + &_eio_error_unlink_cb, + child); + eo_ref(child); + return priv->load.status; +} + +/** + * Children Slice Get + */ +static Efl_Model_Load_Status +_eio_model_efl_model_base_children_slice_get(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, + unsigned start, unsigned count, Eina_Accessor **children_accessor) +{ + Eo *child; + Eina_List *l, *ln, *lr = NULL; + + /** + * children must be already loaded otherwise we do nothing + * and parameter is set to NULL. + */ + + if (!(priv->load.status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN)) + { + /** + * Status should be in either unloaded state or unitialized + * so we simply return without much alarm. + */ + *children_accessor = NULL; + return priv->load.status; + } + + if ((start == 0) && (count == 0)) /* this is full data */ + { + /* + * children_accessor will be set to NULL by + * eina_list_accessor_new if the later fails. + */ + *children_accessor = eina_list_accessor_new(priv->children_list); + } + else /* this is only slice */ + { + ln = eina_list_nth_list(priv->children_list, (start-1)); + if (!ln) + { + *children_accessor = NULL; + ERR("children not found !"); + return priv->load.status; + } + + EINA_LIST_FOREACH(ln, l, child) + { + eo_ref(child); + lr = eina_list_append(lr, child); + if (eina_list_count(lr) == count) + break; + } + // This may leak the children Eina_List. + *children_accessor = eina_list_accessor_new(lr); + } + + return priv->load.status; +} + +/** + * Class definitions + */ +static void +_eio_model_eo_base_constructor(Eo *obj, Eio_Model_Data *priv) +{ + eo_do_super(obj, MY_CLASS, eo_constructor()); + unsigned int i; + priv->obj = obj; + + priv->properties_name = eina_array_new(EIO_MODEL_PROP_LAST); + EINA_SAFETY_ON_NULL_RETURN(priv->properties_name); + for (i = 0; i < EIO_MODEL_PROP_LAST; ++i) + eina_array_push(priv->properties_name, _eio_model_prop_names[i]); + + priv->properties_value[EIO_MODEL_PROP_FILENAME] = eina_value_new(EINA_VALUE_TYPE_STRING); + priv->properties_value[EIO_MODEL_PROP_PATH] = eina_value_new(EINA_VALUE_TYPE_STRING); + priv->properties_value[EIO_MODEL_PROP_MTIME] = eina_value_new(EINA_VALUE_TYPE_TIMEVAL); + priv->properties_value[EIO_MODEL_PROP_IS_DIR] = eina_value_new(EINA_VALUE_TYPE_INT); + priv->properties_value[EIO_MODEL_PROP_IS_LNK] = eina_value_new(EINA_VALUE_TYPE_INT); + priv->properties_value[EIO_MODEL_PROP_SIZE] = eina_value_new(EINA_VALUE_TYPE_INT64); + + priv->load.status = EFL_MODEL_LOAD_STATUS_UNLOADED; + priv->monitor = NULL; +} + +static void +_eio_model_path_set(Eo *obj EINA_UNUSED, Eio_Model_Data *priv, const char *path) +{ + priv->path = strdup(path); + + eina_value_set(priv->properties_value[EIO_MODEL_PROP_PATH], priv->path); + eina_value_set(priv->properties_value[EIO_MODEL_PROP_FILENAME], basename(priv->path)); + + priv->monitor = NULL; + _eio_monitors_list_load(priv); +} + +static void +_eio_model_eo_base_destructor(Eo *obj , Eio_Model_Data *priv) +{ + Eo *child; + unsigned int i; + + if (priv->monitor) + eio_monitor_del(priv->monitor); + + if (priv->properties_name) + eina_array_free(priv->properties_name); + + for (i = 0; i < EIO_MODEL_PROP_LAST; ++i) + { + eina_value_free(priv->properties_value[i]); + } + + EINA_LIST_FREE(priv->children_list, child) + eo_unref(child); + + free(priv->path); + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +#include "eio_model.eo.c" diff --git a/src/lib/eio/eio_model.eo b/src/lib/eio/eio_model.eo new file mode 100644 index 0000000000..67890fa1a2 --- /dev/null +++ b/src/lib/eio/eio_model.eo @@ -0,0 +1,49 @@ +class Eio.Model (Eo.Base, Efl.Model.Base) +{ + legacy_prefix: null; + methods { + children_filter_set { + /*@ Set children filter callback. + This function sets, along with user's private data userdata, + the Eio's Eio_Filter_Direct_Cb which is a mid-step + before receiving the real data. Once in filter + callback we can decide, by returning either EINA_FALSE, to abort + the notification or EINA_TRUE to keep it. + @see Eio.h + @see emodel_children_slice_fetch + @def emodel_children_filter_set + @since 1.11 + @in filter_cb + @in userdata */ + params { + Eio_Filter_Direct_Cb filter_cb; /*@ Filter callback */ + void *userdata; /*@ User's private data */ + } + } + path_set { + /*@ Custom Eio_Model constructor. + @def eio_model_constructor + @since 1.11 + @in path */ + params { + @in const(char)* path; /*@ Root path provided by caller */ + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + Efl.Model.Base.properties.get; + Efl.Model.Base.properties_load; + Efl.Model.Base.property.set; + Efl.Model.Base.property.get; + Efl.Model.Base.load; + Efl.Model.Base.load_status.get; + Efl.Model.Base.unload; + Efl.Model.Base.child_add; + Efl.Model.Base.child_del; + Efl.Model.Base.children_slice.get; + Efl.Model.Base.children_count.get; + Efl.Model.Base.children_load; + } +} diff --git a/src/lib/eio/eio_model.h b/src/lib/eio/eio_model.h new file mode 100644 index 0000000000..1c46d77c45 --- /dev/null +++ b/src/lib/eio/eio_model.h @@ -0,0 +1,45 @@ +/** + * @page emodel_eio_main Emodel_Eio + * + * @date 2014 (created) + * + * @brief Emodel_Eio Library Public API Calls + * + * @section toc Table of Contents + * + * @li @ref emodel_eio_main_intro + * + * @section eo_main_intro Introduction + * + * This module targets file operations using Emodel. + + * + * Recommended reading: + * + * @li @ref Emodel + * @li @ref Eo + * @li @ref Eina + * + * @defgroup Emodel_Eio EIO implementation wrapper for Emodel + * + * @addtogroup Emodel_Eio + * @{ + */ + +#ifndef _EMODEL_EIO_H +#define _EMODEL_EIO_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif +#endif //_EMODEL_EIO_H diff --git a/src/lib/eio/eio_model_private.h b/src/lib/eio/eio_model_private.h new file mode 100644 index 0000000000..0aed0d6a4a --- /dev/null +++ b/src/lib/eio/eio_model_private.h @@ -0,0 +1,57 @@ +#ifndef _EIO_MODEL_PRIVATE_H +#define _EIO_MODEL_PRIVATE_H + +#define PROP_LIST_SIZE 7 + +typedef struct _Eio_Model_Data Eio_Model_Data; +typedef struct _Eio_Model_Monitor_Data Eio_Model_Monitor_Data; + +struct _Eio_Model_Monitor_Data +{ + Ecore_Event_Handler *ecore_child_add_handler[3]; + Ecore_Event_Handler *ecore_child_del_handler[3]; + int mon_event_child_add[3]; /**< plus EIO_MONITOR_ERROR */ + int mon_event_child_del[3]; /**< plus EIO_MONITOR_ERROR */ +}; + +enum { + EIO_MODEL_PROP_FILENAME = 0, + EIO_MODEL_PROP_PATH, + EIO_MODEL_PROP_MTIME, + EIO_MODEL_PROP_IS_DIR, + EIO_MODEL_PROP_IS_LNK, + EIO_MODEL_PROP_SIZE, + EIO_MODEL_PROP_LAST +}; + +static const char* _eio_model_prop_names[] = +{ + [EIO_MODEL_PROP_FILENAME] = "filename", + [EIO_MODEL_PROP_PATH] = "path", + [EIO_MODEL_PROP_MTIME] = "mtime", + [EIO_MODEL_PROP_IS_DIR] = "is_dir", + [EIO_MODEL_PROP_IS_LNK] = "is_lnk", + [EIO_MODEL_PROP_SIZE] = "size" +}; + +struct _Eio_Model_Data +{ + Eo *obj; + char *path; + Eina_Array *properties_name; + Eina_Value *properties_value[EIO_MODEL_PROP_LAST]; + Efl_Model_Load load; + int load_pending; + Eina_List *children_list; + /**< EIO data */ + Eio_File *file; + Eina_Bool is_dir; + Eio_Monitor *monitor; + Eio_Model_Monitor_Data mon; + int cb_count_child_add; /**< monitor reference counter for child add event */ + int cb_count_child_del; /**< monitor reference counter for child del event*/ + Eio_Filter_Direct_Cb filter_cb; + void *filter_userdata; +}; + +#endif diff --git a/src/tests/eio/eio_model_test_file.c b/src/tests/eio/eio_model_test_file.c new file mode 100644 index 0000000000..af5269d033 --- /dev/null +++ b/src/tests/eio/eio_model_test_file.c @@ -0,0 +1,232 @@ +//Compile with: +// gcc -o emodel_test_file emodel_test_file.c `pkg-config --cflags --libs emodel` +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#define EFL_MODEL_TEST_FILENAME_PATH "/tmp" +#define EFL_MODEL_MAX_TEST_CHILDS 16 + +/** + * The following test works however + * it is going to rename (move) the original directory to + * new one so '/tmp' as root dir doesn't work , you'll need to use + * '/tmp/some_other_dir' as root instead. + */ +//#define _RUN_LOCAL_TEST + +struct reqs_t { + /* property change */ + int changed_is_dir; + int changed_is_lnk; + int changed_size; + int changed_mtime; + + /* properties list */ + int proplist_filename; + int proplist_path; + int proplist_mtime; + int proplist_is_dir; + int proplist_is_lnk; + int proplist_size; + + /* misc tests for data or propeties */ + int properties; + int children; + int child_add; + int child_del; +}; + +static struct reqs_t reqs; +static Ecore_Event_Handler *handler; + +static Eina_Bool + exit_func(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *ev) + { + Ecore_Event_Signal_Exit *e; + + e = (Ecore_Event_Signal_Exit *)ev; + if (e->interrupt) fprintf(stdout, "Exit: interrupt\n"); + else if (e->quit) fprintf(stdout, "Exit: quit\n"); + else if (e->terminate) fprintf(stdout, "Exit: terminate\n"); + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; + } + +static Eina_Bool +_load_status_cb(void *data EINA_UNUSED, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + Efl_Model_Load *st = event_info; + printf("Load CHANGE\n"); + + if (st->status & EFL_MODEL_LOAD_STATUS_LOADED_CHILDREN) + printf("Children is Loaded\n"); + + if (st->status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES) + printf("Properties is Loaded\n"); + + if ((st->status & EFL_MODEL_LOAD_STATUS_LOADED) == EFL_MODEL_LOAD_STATUS_LOADED) + { + Eina_Accessor *accessor; + const Eina_Value *value_prop; + Efl_Model_Load_Status status; + unsigned int total; + char *str; + + printf("Model is Loaded\n"); + eo_do(obj, status = efl_model_property_get("filename", &value_prop)); + str = eina_value_to_string(value_prop); + printf("efl_model_loaded filename %s, status=%d\n", str, status); + free(str); + + eo_do(obj, status = efl_model_property_get("size", &value_prop)); + str = eina_value_to_string(value_prop); + printf("efl_model_loaded size %s, status=%d\n", str, status); + free(str); + + eo_do(obj, status = efl_model_property_get("mtime", &value_prop)); + str = eina_value_to_string(value_prop); + printf("efl_model_loaded mtime %s, status=%d\n", str, status); + free(str); + + eo_do(obj, efl_model_children_count_get(&total)); + printf("efl_model_test count %d\n", (int)total); + + /**< get full list */ + eo_do(obj, status = efl_model_children_slice_get(0 ,0 ,(Eina_Accessor **)&accessor)); + eina_accessor_free(accessor); + eo_do(obj, status = efl_model_children_slice_get(5 ,5 ,(Eina_Accessor **)&accessor)); + eina_accessor_free(accessor); + ecore_main_loop_quit(); + } + return EINA_TRUE; +} + +static Eina_Bool +_properties_change_cb(void *data EINA_UNUSED, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + const Efl_Model_Property_Event *evt = (Efl_Model_Property_Event *)event_info; + const char *prop; + Eina_Array_Iterator it; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(evt->changed_properties, i, prop, it) + { + if (!strcmp(prop, "is_dir")) + reqs.changed_is_dir = 1; + else if (!strcmp(prop, "is_lnk")) + reqs.changed_is_lnk = 1; + else if (!strcmp(prop, "size")) + reqs.changed_size = 1; + else if (!strcmp(prop, "mtime")) + reqs.changed_mtime = 1; + } + + reqs.properties = 1; + return EINA_TRUE; +} + +static Eina_Bool +_children_count_cb(void *data EINA_UNUSED, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + unsigned int *len = (unsigned int *)event_info; + unsigned int total; + + fprintf(stdout, "Children count number=%d\n", *len); + reqs.children = *len; + + eo_do(obj, efl_model_children_count_get(&total)); + fprintf(stdout, "New total children count number=%d\n", *len); + + return EINA_TRUE; +} + +START_TEST(eio_model_test_test_file) +{ + Eo *filemodel = NULL; + const Eina_Value *value_prop; + Efl_Model_Load_Status status; +#ifdef _RUN_LOCAL_TEST + Eina_Value nameset_value; +#endif + Eina_Array *properties_list; + Eina_Array_Iterator iterator; + char *str; + unsigned int i; + + memset(&reqs, -1, sizeof(struct reqs_t)); + + fail_if(!eina_init(), "ERROR: Cannot init Eina!\n"); + fail_if(!ecore_init(), "ERROR: Cannot init Ecore!\n"); + fail_if(!eio_init(), "ERROR: Cannot init EIO!\n"); + + filemodel = eo_add(EIO_MODEL_CLASS, NULL, eio_model_path_set(EFL_MODEL_TEST_FILENAME_PATH)); + fail_if(!filemodel, "ERROR: Cannot init model!\n"); + + eo_do(filemodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _load_status_cb, NULL)); + eo_do(filemodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_PROPERTIES_CHANGED, _properties_change_cb, NULL)); + eo_do(filemodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, _children_count_cb, NULL)); + + eo_do(filemodel, efl_model_load()); + + handler = ecore_event_handler_add(ECORE_EVENT_SIGNAL_EXIT, exit_func, NULL); + + eo_do(filemodel, status = efl_model_property_get("filename", &value_prop)); + str = eina_value_to_string(value_prop); + printf("efl_model_test filename %s, load status %d\n", str, status); + + free(str); + + i = 0; + eo_do(filemodel, efl_model_properties_get(&properties_list)); + EINA_ARRAY_ITER_NEXT(properties_list, i, str, iterator) + { + fprintf(stdout, "Returned property list %d: %s\n", i, str); + if(!strcmp(str, "filename")) + reqs.proplist_filename = 1; + else if(!strcmp(str, "path")) + reqs.proplist_path = 1; + else if(!strcmp(str, "mtime")) + reqs.proplist_mtime = 1; + else if(!strcmp(str, "is_dir")) + reqs.proplist_is_dir = 1; + else if(!strcmp(str, "is_lnk")) + reqs.proplist_is_lnk = 1; + else if(!strcmp(str, "size")) + reqs.proplist_size = 1; + } + + ecore_main_loop_begin(); + +#ifdef _RUN_LOCAL_TEST + eina_value_setup(&nameset_value, EINA_VALUE_TYPE_STRING); + eina_value_setup(&value_prop, EINA_VALUE_TYPE_STRING); + eina_value_set(&nameset_value, "/tmp/efl_model_test"); + eo_do(filemodel, efl_model_property_set("path", &nameset_value)); + eina_value_flush(&nameset_value); + eo_do(filemodel, status = efl_model_property_get("path", &value_prop)); +#endif + + sleep(1); /**< EIO is asynchrounous so I must give some time for deletions to execute */ + eo_unref(filemodel); + ecore_shutdown(); + eina_shutdown(); + eio_shutdown(); +} +END_TEST + +void +eio_model_test_file(TCase *tc) +{ + tcase_add_test(tc, eio_model_test_test_file); +} + diff --git a/src/tests/eio/eio_model_test_monitor_add.c b/src/tests/eio/eio_model_test_monitor_add.c new file mode 100644 index 0000000000..af95ae8f75 --- /dev/null +++ b/src/tests/eio/eio_model_test_monitor_add.c @@ -0,0 +1,125 @@ +//Compile with: +// gcc -o emodel_test_file emodel_test_file.c `pkg-config --cflags --libs emodel` +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#define EFL_MODEL_TEST_FILENAME_PATH "/tmp" + +Eina_Bool children_added = EINA_FALSE; + +static Eina_Bool +_load_monitor_status_cb(void *data, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + Efl_Model_Load* st = event_info; + Eo* parent = data; + const Eina_Value* value_prop = NULL; + const char* str = NULL; + + if (!(st->status & EFL_MODEL_LOAD_STATUS_LOADED_PROPERTIES)) + return EINA_TRUE; + + eo_do(obj, efl_model_property_get("filename", &value_prop)); + fail_if(!value_prop, "ERROR: Cannot get property!\n"); + + str = eina_value_to_string(value_prop); + fail_if(!str, "ERROR: Cannot convert value to string!\n"); + fprintf(stderr, "new children filename %s\n", str); + if(strcmp(str, "test_file_monitor_add") == 0) + { + fprintf(stderr, "is child that we want\n"); + eo_do(obj, eo_event_callback_del(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _load_monitor_status_cb, data)); + children_added = EINA_TRUE; + eo_do(parent, efl_model_child_del(obj)); + ecore_main_loop_quit(); + } + + return EINA_FALSE; +} + +static Eina_Bool +_children_added_cb(void *data EINA_UNUSED, Eo *obj EINA_UNUSED, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + Efl_Model_Children_Event* evt = event_info; + if (evt == NULL) + return EINA_TRUE; + + eo_do(evt->child, eo_event_callback_add(EFL_MODEL_BASE_EVENT_LOAD_STATUS, _load_monitor_status_cb, obj)); + eo_do(evt->child, efl_model_load()); + + return EINA_TRUE; +} + +static Eina_Bool +_children_count_cb(void *data EINA_UNUSED, Eo *obj, const Eo_Event_Description *desc EINA_UNUSED, void *event_info) +{ + unsigned int *len = event_info; + Eina_Accessor *accessor; + Efl_Model_Load_Status status; + Eo *child; + unsigned int i = 0; + + fprintf(stderr, "Children count number=%d\n", *len); + + /**< get full list */ + eo_do(obj, status = efl_model_children_slice_get(0 ,0 ,(Eina_Accessor **)&accessor)); + if(accessor != NULL) + { + EINA_ACCESSOR_FOREACH(accessor, i, child) {} + fprintf(stdout, "Got %d childs from Accessor. status=%d\n", i, status); + } + + fclose(fopen(EFL_MODEL_TEST_FILENAME_PATH "/test_file_monitor_add", "w+")); + + return EINA_TRUE; +} + +START_TEST(eio_model_test_test_monitor_add) +{ + Eo *filemodel = NULL; + + fprintf(stderr, "efl_model_test_test_monitor_add\n"); + + fail_if(!eina_init(), "ERROR: Cannot init Eina!\n"); + fail_if(!ecore_init(), "ERROR: Cannot init Ecore!\n"); + fail_if(!eio_init(), "ERROR: Cannot init EIO!\n"); + + filemodel = eo_add(EIO_MODEL_CLASS, NULL, eio_model_path_set(EFL_MODEL_TEST_FILENAME_PATH)); + fail_if(!filemodel, "ERROR: Cannot init model!\n"); + + eo_do(filemodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_CHILD_ADDED, _children_added_cb, NULL)); + eo_do(filemodel, eo_event_callback_add(EFL_MODEL_BASE_EVENT_CHILDREN_COUNT_CHANGED, _children_count_cb, NULL)); + + eo_do(filemodel, efl_model_load()); + + ecore_main_loop_begin(); + + sleep(1); /**< EIO is asynchrounous so I must give some time for deletions to execute */ + + ecore_main_loop_iterate(); /**< Give time to unlink file */ + + eo_unref(filemodel); + + eio_shutdown(); + ecore_shutdown(); + eina_shutdown(); + + fail_if(!children_added); +} +END_TEST + +void +eio_model_test_monitor_add(TCase *tc) +{ + tcase_add_test(tc, eio_model_test_test_monitor_add); +} + diff --git a/src/tests/eio/eio_suite.c b/src/tests/eio/eio_suite.c index 08deb05803..ecc52247b3 100644 --- a/src/tests/eio/eio_suite.c +++ b/src/tests/eio/eio_suite.c @@ -19,6 +19,8 @@ struct _Eio_Test_Case static const Eio_Test_Case etc[] = { {"Eio_Monitor", eio_test_monitor}, + {"Eio Model", eio_model_test_file}, + {"Eio Model Monitor", eio_model_test_monitor_add}, {NULL, NULL} }; diff --git a/src/tests/eio/eio_suite.h b/src/tests/eio/eio_suite.h index 2212f8eafb..d59a2ab6c0 100644 --- a/src/tests/eio/eio_suite.h +++ b/src/tests/eio/eio_suite.h @@ -4,5 +4,7 @@ #include void eio_test_monitor(TCase *tc); +void eio_model_test_file(TCase *tc); +void eio_model_test_monitor_add(TCase *tc); #endif /* _EIO_SUITE_H */