918 lines
29 KiB
C
918 lines
29 KiB
C
/* Copyright (C) 2008-2020 Davide Andreoli (see AUTHORS)
|
|
*
|
|
* This file is part of edgar.
|
|
* edgar is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* edgar is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with edgar. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include <Python.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include <e.h>
|
|
#include <Efl_Ui.h>
|
|
|
|
#include "edgar_config.h"
|
|
#include "e_mod_main.h"
|
|
#include "e_mod_edgar.h"
|
|
#include "efl.eo_api.h"
|
|
|
|
|
|
/*
|
|
TODO
|
|
- gadget configurations (and thus id management)
|
|
- gadget actions (keybindings)
|
|
*/
|
|
|
|
/* Local typedefs */
|
|
typedef struct {
|
|
const char *name; // ex: "ruler" (from folder name)
|
|
const char *label; // ex: "ruler" (from __gadget_name__)
|
|
const char *path; // ex: "/usr/local/lib/enlightenment/gadgets/ruler"
|
|
const char *edjefile; // ex: "/usr/local/lib/enlightenment/gadgets/ruler/ruler.edj"
|
|
PyObject *mod; // the python module for this gadget
|
|
PyObject *instance; // the python Gadget class instance
|
|
Eina_List *pops_obj; // list of edje objs that are set as popup content
|
|
E_Gadcon_Client_Class *cclass;
|
|
Eina_Bool opt_pop_on_desk;
|
|
}Edgar_Py_Gadget;
|
|
|
|
|
|
/* Local Prototypes */
|
|
static Edgar_Py_Gadget *edgar_gadget_load(const char *name, const char *path);
|
|
static void edgar_gadget_unload(Edgar_Py_Gadget *gadget);
|
|
static void edgar_gadgets_load_all(const char *path);
|
|
static void edgar_gadgets_hash_free_func(void *data);
|
|
static Eina_Bool edgar_theme_object_set(Edgar_Py_Gadget *gadget, Evas_Object *obj, const char *group);
|
|
|
|
/* Local Gadcon Prototypes */
|
|
static E_Gadcon_Client *_edgar_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style);
|
|
static void _edgar_gc_shutdown(E_Gadcon_Client *gcc);
|
|
static void _edgar_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient);
|
|
static const char *_edgar_gc_label(const E_Gadcon_Client_Class *client_class);
|
|
static const char *_edgar_gc_id_new(const E_Gadcon_Client_Class *client_class);
|
|
static void _edgar_gc_id_del(const E_Gadcon_Client_Class *client_class, const char *id);
|
|
static Evas_Object *_edgar_gc_icon(const E_Gadcon_Client_Class *client_class, Evas *evas);
|
|
|
|
/* Python eapi module proto */
|
|
PyMODINIT_FUNC PyInit_eapi(void);
|
|
|
|
|
|
/* Local Variables */
|
|
static void *py3_lib_handle;
|
|
static const char *edgar_gadgets_system_dir; //ex: "/usr/lib/enlightenment/gadgets/"
|
|
static Eina_Hash *edgar_gadgets; //key: "gadget_name" val: Edgar_Py_Gadget*
|
|
static PyObject *evasObjectType;
|
|
static PyObject *edjeEdjeType;
|
|
|
|
|
|
#define CURRENT_VAPI 2
|
|
|
|
#define PY_BOOTSTRAP_SCRIPT \
|
|
"import sys \n" \
|
|
"sys.path.append('%s') \n" \
|
|
"\n"
|
|
|
|
|
|
#define PY_ON_ERROR_RETURN(_extra_check_, _ret_val_, _msg_) \
|
|
if (PyErr_Occurred()) { \
|
|
PyErr_Print(); \
|
|
PyRun_SimpleString("sys.stdout.flush()"); \
|
|
PyRun_SimpleString("sys.stderr.flush()"); \
|
|
if (_msg_) DBG("EDGAR: ERROR "_msg_); \
|
|
return _ret_val_; \
|
|
} \
|
|
if (_extra_check_) { \
|
|
if (_msg_) DBG("EDGAR: ERROR "_msg_); \
|
|
return _ret_val_; \
|
|
} \
|
|
|
|
#define PY_ON_ERROR_GOTO(_extra_check_, _goto_, _msg_) \
|
|
if (PyErr_Occurred()) { \
|
|
PyErr_Print(); \
|
|
PyRun_SimpleString("sys.stdout.flush()"); \
|
|
PyRun_SimpleString("sys.stderr.flush()"); \
|
|
if (_msg_) DBG("EDGAR: ERROR "_msg_); \
|
|
goto _goto_; \
|
|
} \
|
|
if (_extra_check_) { \
|
|
if (_msg_) DBG("EDGAR: ERROR "_msg_); \
|
|
goto _goto_; \
|
|
} \
|
|
|
|
|
|
#if (PY_VERSION_HEX < 0x03030000)
|
|
#define PyString_AsString(x) PyBytes_AsString(PyUnicode_AsUTF8String(x))
|
|
#else
|
|
#define PyString_AsString PyUnicode_AsUTF8
|
|
#endif
|
|
|
|
|
|
/*****************************************************************************/
|
|
/***** Main stuff ***********************************************************/
|
|
/*****************************************************************************/
|
|
|
|
Eina_Bool
|
|
edgar_init()
|
|
{
|
|
char buf[PATH_MAX];
|
|
int ret;
|
|
|
|
// Make cpython visible to gadgets and to gadgets imported modules
|
|
// NOTE: This is needed because e load modules using RTLD_LOCAL
|
|
// NOTE: PYINSTSONAME is gathered at build time from the sysconfig module
|
|
py3_lib_handle = dlopen(PYINSTSONAME, (RTLD_LAZY | RTLD_GLOBAL));
|
|
if (!py3_lib_handle)
|
|
{
|
|
e_util_dialog_internal("Edgar Error",
|
|
"<font align=left><b>Cannot find Python3 lib.</b><br>"
|
|
"Your python gadgets will not work.<br><br>"
|
|
"Please make sure Python3 is installed.</font>");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
// prepare the local scope
|
|
snprintf(buf, sizeof(buf), "%s/enlightenment/edgar_gadgets", e_prefix_lib_get());
|
|
edgar_gadgets_system_dir = eina_stringshare_add(buf);
|
|
edgar_gadgets = eina_hash_string_superfast_new(edgar_gadgets_hash_free_func);
|
|
|
|
// Add the eapi module as a built-in module
|
|
PyImport_AppendInittab("eapi", PyInit_eapi);
|
|
|
|
// init python
|
|
/// TOTRY Py_SetPath()
|
|
Py_Initialize();
|
|
|
|
// This import the C functions from the efl eo api.
|
|
// The module do NOT need to be linked with the .so files, python
|
|
// import machinery is used here.
|
|
// Used functions: object_from_instance() and instance_from_object()
|
|
if (import_efl__eo() != 0)
|
|
{
|
|
DBG("EDGAR: Cannot import python-efl");
|
|
if (PyErr_Occurred())
|
|
PyErr_Print();
|
|
e_util_dialog_internal("Edgar Error",
|
|
"<font align=left><b>Python-efl not found.</b><br>"
|
|
"Your python gadgets will not work.<br><br>"
|
|
"Please install or update <i>python-efl</i>.</font>");
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
// keep some efl types around, will be used for type-checking
|
|
PyObject *module, *moduleDict;
|
|
|
|
module = PyImport_ImportModule("efl.evas");
|
|
moduleDict = PyModule_GetDict(module);
|
|
evasObjectType = PyDict_GetItemString(moduleDict, "Object");
|
|
Py_DECREF(module);
|
|
|
|
module = PyImport_ImportModule("efl.edje");
|
|
/// TO TRY: PyObject * pClass = PyObject_GetAttrString(pModule, "Grasp_Behavior");
|
|
moduleDict = PyModule_GetDict(module);
|
|
edjeEdjeType = PyDict_GetItemString(moduleDict, "Edje");
|
|
Py_DECREF(module);
|
|
|
|
// Run the bootstrap python code
|
|
snprintf(buf, sizeof(buf), PY_BOOTSTRAP_SCRIPT, edgar_gadgets_system_dir);
|
|
DBG("EDGAR: Bootstrap (sys.path: '%s')", edgar_gadgets_system_dir);
|
|
ret = PyRun_SimpleString(buf);
|
|
PY_ON_ERROR_RETURN(ret != 0, EINA_FALSE, "Python bootstrap failed!");
|
|
DBG("EDGAR: Bootstrap successful");
|
|
|
|
// Load all the available gadgets
|
|
edgar_gadgets_load_all(edgar_gadgets_system_dir);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
void
|
|
edgar_shutdown()
|
|
{
|
|
DBG("EDGAR: Shutdown started ...");
|
|
|
|
// unload and free all the gadgets
|
|
eina_hash_free(edgar_gadgets);
|
|
|
|
// shutdown python
|
|
// ...still getting some strange segfault here :/
|
|
// if (Py_IsInitialized())
|
|
// Py_Finalize();
|
|
|
|
// clean local scope
|
|
eina_stringshare_del(edgar_gadgets_system_dir);
|
|
|
|
if (py3_lib_handle)
|
|
dlclose(py3_lib_handle);
|
|
|
|
DBG("EDGAR: Shutdown completed!");
|
|
}
|
|
|
|
static void
|
|
edgar_gadgets_load_all(const char *path)
|
|
{
|
|
Eina_List *files;
|
|
char buf[PATH_MAX];
|
|
char *filename;
|
|
|
|
DBG("EDGAR: Seaching python gadgets in %s", path);
|
|
files = ecore_file_ls(path);
|
|
EINA_LIST_FREE(files, filename)
|
|
{
|
|
snprintf(buf, sizeof(buf), "%s/%s", path, filename);
|
|
if (ecore_file_is_dir(buf))
|
|
edgar_gadget_load(filename, buf);
|
|
|
|
free(filename);
|
|
}
|
|
eina_list_free(files);
|
|
}
|
|
|
|
static Edgar_Py_Gadget*
|
|
edgar_gadget_load(const char *name, const char *path)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
E_Gadcon_Client_Class *cclass;
|
|
char fname[PATH_MAX + 100]; // +100 to silence warnings :/
|
|
const char *label;
|
|
PyObject *mod, *attr, *opts;
|
|
long vapi = 0;
|
|
|
|
// check the given path
|
|
snprintf(fname, sizeof(fname), "%s/__init__.pyc", path);
|
|
if (!ecore_file_exists(fname))
|
|
{
|
|
snprintf(fname, sizeof(fname), "%s/__init__.py", path);
|
|
if (!ecore_file_exists(fname))
|
|
return NULL;
|
|
}
|
|
|
|
// import the gadget in python
|
|
DBG("EDGAR: * importing gadget: '%s' from: '%s'", name, fname);
|
|
mod = PyImport_ImportModule(name);
|
|
PY_ON_ERROR_RETURN(mod == NULL, NULL, "Gadget import failed!");
|
|
|
|
// check the api version
|
|
attr = PyObject_GetAttrString(mod, "__gadget_vapi__");
|
|
if ((attr == NULL) || (!PyLong_Check(attr)) ||
|
|
((vapi = PyLong_AsLong(attr)) != CURRENT_VAPI))
|
|
{
|
|
e_util_dialog_show("Edgar Error",
|
|
"<font align=left><b>The %s gadget is outdated !</b><br><br>"
|
|
"Please update the gadget or contact the gadget maintainer.<br><br>"
|
|
"current edgar api: %d<br>"
|
|
"gadget api version: %ld<br>"
|
|
"</font>", name, CURRENT_VAPI, vapi);
|
|
return NULL;
|
|
}
|
|
Py_DECREF(attr);
|
|
|
|
// cache the label
|
|
attr = PyObject_GetAttrString(mod, "__gadget_name__");
|
|
PY_ON_ERROR_RETURN(attr == NULL, NULL, "Gadget name not found");
|
|
label = eina_stringshare_add(PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
|
|
//Alloc & populate the gc_class
|
|
cclass = E_NEW(E_Gadcon_Client_Class, 1);
|
|
if (!cclass) return NULL;
|
|
cclass->version = GADCON_CLIENT_CLASS_VERSION;
|
|
cclass->name = eina_stringshare_add(name);
|
|
cclass->default_style = eina_stringshare_add("plain");
|
|
cclass->func.init = _edgar_gc_init;
|
|
cclass->func.shutdown = _edgar_gc_shutdown;
|
|
cclass->func.orient = _edgar_gc_orient;
|
|
cclass->func.label = _edgar_gc_label;
|
|
cclass->func.icon = _edgar_gc_icon;
|
|
cclass->func.id_new = _edgar_gc_id_new;
|
|
cclass->func.id_del = _edgar_gc_id_del;
|
|
|
|
//Alloc & populate the gadget struct
|
|
gadget = E_NEW(Edgar_Py_Gadget, 1);
|
|
if (!gadget) return NULL;
|
|
gadget->label = label;
|
|
gadget->name = eina_stringshare_add(name);
|
|
gadget->path = eina_stringshare_add(path);
|
|
gadget->cclass = cclass;
|
|
gadget->mod = mod;
|
|
gadget->opt_pop_on_desk = EINA_FALSE;
|
|
// do gadget have a local edj ?
|
|
snprintf(fname, sizeof(fname), "%s/%s.edj", path, name);
|
|
if (ecore_file_exists(fname))
|
|
gadget->edjefile = eina_stringshare_add(fname);
|
|
else
|
|
gadget->edjefile = NULL;
|
|
|
|
// read the gadget options dict
|
|
if (PyObject_HasAttrString(mod, "__gadget_opts__") &&
|
|
(opts = PyObject_GetAttrString(mod, "__gadget_opts__")))
|
|
{
|
|
attr = PyDict_GetItemString(opts, "popup_on_desktop");
|
|
if (attr && PyObject_IsTrue(attr))
|
|
gadget->opt_pop_on_desk = EINA_TRUE;
|
|
|
|
Py_DECREF(opts);
|
|
}
|
|
|
|
//Register the new class
|
|
e_gadcon_provider_register(cclass);
|
|
eina_hash_add(edgar_gadgets, name, gadget);
|
|
|
|
return gadget;
|
|
}
|
|
|
|
static void
|
|
edgar_gadget_unload(Edgar_Py_Gadget *gadget)
|
|
{
|
|
Evas_Object *popup_content;
|
|
Eina_List *l, *l2;
|
|
|
|
DBG("EDGAR: Unload gadget: %s", gadget->name);
|
|
|
|
// kill all the active popups edje obj
|
|
EINA_LIST_FOREACH_SAFE(gadget->pops_obj, l, l2, popup_content)
|
|
E_FREE_FUNC(popup_content, evas_object_del);
|
|
|
|
// Free the gadcon client class
|
|
e_gadcon_provider_unregister(gadget->cclass);
|
|
eina_stringshare_del(gadget->cclass->name);
|
|
eina_stringshare_del(gadget->cclass->default_style);
|
|
free(gadget->cclass);
|
|
|
|
// Free the gadget itself
|
|
eina_stringshare_del(gadget->name);
|
|
eina_stringshare_del(gadget->label);
|
|
eina_stringshare_del(gadget->path);
|
|
eina_stringshare_del(gadget->edjefile);
|
|
Py_XDECREF(gadget->instance);
|
|
Py_XDECREF(gadget->mod);
|
|
free(gadget);
|
|
}
|
|
|
|
static void
|
|
edgar_gadgets_hash_free_func(void *data)
|
|
{
|
|
Edgar_Py_Gadget *gadget = data;
|
|
edgar_gadget_unload(gadget);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/***** Gadgets Utils ******************************************************/
|
|
/*****************************************************************************/
|
|
static Evas_Object *
|
|
edgar_request_popup_content(Edgar_Py_Gadget *gadget, Evas_Object *parent)
|
|
{
|
|
PyObject *pyparent = NULL, *pycontent = NULL, *pylist = NULL;
|
|
Evas_Object *content = NULL;
|
|
|
|
pyparent = object_from_instance(parent);
|
|
PY_ON_ERROR_GOTO(!pyparent, exit_error, "Cannot convert parent");
|
|
|
|
// call the popup_created() method of the gadget.
|
|
pycontent = PyObject_CallMethod(gadget->instance, "popup_created",
|
|
"(S)", pyparent);
|
|
PY_ON_ERROR_GOTO(!pycontent, exit_error, "Cannot call popup_created()");
|
|
Py_DECREF(pyparent);
|
|
|
|
// None is a valid return, as per default implementation
|
|
if (pycontent == Py_None) goto exit_error;
|
|
|
|
// check the returned content is a valid elm object
|
|
content = instance_from_object(pycontent);
|
|
if (!content || !efl_isa(content, EFL_UI_WIDGET_CLASS))
|
|
{
|
|
DBG("EDGAR: Error: popup content is not a elm widget");
|
|
goto exit_error;
|
|
}
|
|
//DBG("EDGAR: ret content %p %s", content, efl_class_name_get(content));
|
|
|
|
// put the content in the _popups list of the gadget
|
|
pylist = PyObject_GetAttrString(gadget->instance, "_popups");
|
|
if (!pylist || !PyList_Check(pylist) || PyList_Append(pylist, pycontent) != 0)
|
|
DBG("EDGAR: Warning: cannot append the popup content to Gadget._popups");
|
|
Py_XDECREF(pylist);
|
|
Py_DECREF(pycontent);
|
|
|
|
// keep track of the popup object
|
|
gadget->pops_obj = eina_list_append(gadget->pops_obj, content);
|
|
|
|
return content;
|
|
|
|
exit_error:
|
|
Py_XDECREF(pycontent);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
edgar_notify_popup_deleted(Edgar_Py_Gadget *gadget, Evas_Object *popup_content)
|
|
{
|
|
PyObject *pyobj = object_from_instance(popup_content);
|
|
|
|
// remove the content object from the gadget _popups list
|
|
PyObject *list = PyObject_GetAttrString(gadget->instance, "_popups");
|
|
if (list && PyList_Check(list) && PySequence_Check(list))
|
|
{
|
|
Py_ssize_t index = PySequence_Index(list, pyobj);
|
|
if (index >= 0) PySequence_DelItem(list, index);
|
|
}
|
|
Py_XDECREF(list);
|
|
|
|
// call the popup_destoyed() method of the gadget.
|
|
PyObject *ret = PyObject_CallMethod(gadget->instance, "popup_destroyed",
|
|
"(S)", pyobj);
|
|
PY_ON_ERROR_RETURN(!ret, , "Cannot call popup_destroyed()");
|
|
Py_DECREF(ret);
|
|
Py_DECREF(pyobj);
|
|
|
|
// remove popup references
|
|
gadget->pops_obj = eina_list_remove(gadget->pops_obj, popup_content);
|
|
}
|
|
|
|
static Eina_Bool
|
|
edgar_theme_object_set(Edgar_Py_Gadget *gadget, Evas_Object *obj, const char *group)
|
|
{
|
|
char buf[PATH_MAX];
|
|
|
|
if (!gadget || !obj || !group)
|
|
return EINA_FALSE;
|
|
|
|
snprintf(buf, sizeof(buf), "e/gadgets/%s/%s", gadget->name, group);
|
|
|
|
// TODO search in E theme
|
|
|
|
// search in gadget local edje file
|
|
if (!gadget->edjefile)
|
|
return EINA_FALSE;
|
|
|
|
if (efl_isa(obj, EFL_CANVAS_LAYOUT_CLASS))
|
|
return edje_object_file_set(obj, gadget->edjefile, buf);
|
|
|
|
if (efl_isa(obj, EFL_UI_LAYOUT_CLASS))
|
|
return elm_layout_file_set(obj, gadget->edjefile, buf);
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static void
|
|
edgar_show_gadget_info(Edgar_Py_Gadget *gadget)
|
|
{
|
|
Evas_Object *popup, *table, *icon, *entry;
|
|
Eina_Strbuf *strbuf;
|
|
PyObject *attr;
|
|
|
|
// build the text
|
|
strbuf = eina_strbuf_new();
|
|
if ((attr = PyObject_GetAttrString(gadget->mod, "__gadget_name__")))
|
|
{
|
|
eina_strbuf_append_printf(strbuf, "<title>%s</title>",
|
|
PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
}
|
|
if ((attr = PyObject_GetAttrString(gadget->mod, "__gadget_vers__")))
|
|
{
|
|
eina_strbuf_append_printf(strbuf, " v%s", PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
}
|
|
if ((attr = PyObject_GetAttrString(gadget->mod, "__gadget_auth__")))
|
|
{
|
|
eina_strbuf_append_printf(strbuf, "<br>Author: %s", PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
}
|
|
if ((attr = PyObject_GetAttrString(gadget->mod, "__gadget_mail__")))
|
|
{
|
|
eina_strbuf_append_printf(strbuf, "<br>Contact: %s", PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
}
|
|
if ((attr = PyObject_GetAttrString(gadget->mod, "__gadget_desc__")))
|
|
{
|
|
eina_strbuf_append_printf(strbuf, "<br><br>%s", PyString_AsString(attr));
|
|
Py_DECREF(attr);
|
|
}
|
|
|
|
// popup
|
|
popup = elm_popup_add(e_comp->elm);
|
|
E_EXPAND(popup);
|
|
evas_object_layer_set(popup, E_LAYER_POPUP);
|
|
elm_popup_allow_events_set(popup, EINA_TRUE);
|
|
elm_popup_scrollable_set(popup, EINA_FALSE);
|
|
|
|
table = elm_table_add(popup);
|
|
elm_table_padding_set(table, 10 * e_scale, 0);
|
|
E_EXPAND(table);
|
|
evas_object_show(table);
|
|
elm_object_content_set(popup, table);
|
|
|
|
// icon
|
|
icon = edje_object_add(evas_object_evas_get(table));
|
|
edgar_theme_object_set(gadget, icon, "icon");
|
|
evas_object_size_hint_min_set(icon, 64 * e_scale, 64 * e_scale);
|
|
elm_table_pack(table, icon, 0, 0, 1, 1);
|
|
evas_object_show(icon);
|
|
|
|
// text
|
|
entry = elm_entry_add(table);
|
|
elm_entry_editable_set(entry, EINA_FALSE);
|
|
elm_object_text_set(entry, eina_strbuf_string_get(strbuf));
|
|
E_EXPAND(entry); E_FILL(entry);
|
|
elm_table_pack(table, entry, 1, 0, 1, 1);
|
|
evas_object_show(entry);
|
|
|
|
// show the popup
|
|
E_Zone *zone = e_zone_current_get();
|
|
popup = e_comp_object_util_add(popup, E_COMP_OBJECT_TYPE_NONE);
|
|
evas_object_layer_set(popup, E_LAYER_POPUP);
|
|
evas_object_move(popup, zone->x, zone->y);
|
|
evas_object_resize(popup, zone->w / 3, zone->h / 3);
|
|
e_comp_object_util_center(popup);
|
|
evas_object_show(popup);
|
|
e_comp_object_util_autoclose(popup, NULL, e_comp_object_util_autoclose_on_escape, NULL);
|
|
|
|
eina_strbuf_free(strbuf);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/***** Gadcon (old) Popup *************************************************/
|
|
/*****************************************************************************/
|
|
static void edgar_popup_del_cb(void *data, const Efl_Event *event)
|
|
{
|
|
Eo *obj = event->object;
|
|
|
|
Edgar_Py_Gadget *gadget = data;
|
|
edgar_notify_popup_deleted(gadget, obj);
|
|
}
|
|
|
|
static E_Gadcon_Popup *
|
|
edgar_popup_new(Edgar_Py_Gadget *gadget, E_Gadcon_Client *gcc)
|
|
{
|
|
E_Gadcon_Popup *popup;
|
|
Evas_Object *content;
|
|
|
|
// create the gadcon popup to hold the content generated by the gadget
|
|
popup = e_gadcon_popup_new(gcc, EINA_FALSE);
|
|
|
|
// request the popup content from the python gadget
|
|
content = edgar_request_popup_content(gadget, popup->comp_object);
|
|
if (!content)
|
|
{
|
|
e_object_del(E_OBJECT(popup));
|
|
return NULL;
|
|
}
|
|
|
|
// NOTE: del cb with priority to be called before the python-efl one.
|
|
// Otherwise python-efl delete the python obj too soon
|
|
efl_event_callback_priority_add(content, EFL_EVENT_DEL,
|
|
EFL_CALLBACK_PRIORITY_BEFORE, edgar_popup_del_cb, gadget);
|
|
|
|
// put the popup content in the gadcon popup and show it
|
|
e_gadcon_popup_content_set(popup, content);
|
|
e_gadcon_popup_show(popup);
|
|
|
|
return popup;
|
|
}
|
|
|
|
static void
|
|
edgar_menu_info_cb(void *data, E_Menu *m, E_Menu_Item *mi)
|
|
{
|
|
edgar_show_gadget_info(data);
|
|
}
|
|
|
|
static void
|
|
edgar_mouse_down3_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
|
{
|
|
E_Gadcon_Client *gcc = data;
|
|
Evas_Event_Mouse_Down *ev = event_info;
|
|
Edgar_Py_Gadget *gadget = gcc->data;
|
|
|
|
if (ev->button == 3)
|
|
{
|
|
E_Zone *zone;
|
|
E_Menu *m;
|
|
int x, y;
|
|
|
|
zone = e_zone_current_get();
|
|
m = e_menu_new();
|
|
|
|
E_Menu_Item *mi;
|
|
mi = e_menu_item_new(m);
|
|
e_menu_item_label_set(mi, "Gadget info");
|
|
e_util_menu_item_theme_icon_set(mi, "help-about");
|
|
e_menu_item_callback_set(mi, edgar_menu_info_cb, gadget);
|
|
|
|
m = e_gadcon_client_util_menu_items_append(gcc, m, 0);
|
|
|
|
e_gadcon_canvas_zone_geometry_get(gcc->gadcon, &x, &y, NULL, NULL);
|
|
e_menu_activate_mouse(m, zone, x + ev->output.x, y + ev->output.y,
|
|
1, 1, E_MENU_POP_DIRECTION_AUTO, ev->timestamp);
|
|
evas_event_feed_mouse_up(gcc->gadcon->evas, ev->button,
|
|
EVAS_BUTTON_NONE, ev->timestamp, NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
edgar_mouse_down1_cb(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
|
{
|
|
E_Gadcon_Client *gcc = data;
|
|
Evas_Event_Mouse_Down *ev = event_info;
|
|
Edgar_Py_Gadget *gadget = gcc->data;
|
|
E_Gadcon_Popup *popup;
|
|
|
|
if (ev->button == 1)
|
|
{
|
|
if ((popup = evas_object_data_get(obj, "popup")))
|
|
{
|
|
E_FREE_FUNC(popup, e_object_del);
|
|
evas_object_data_del(obj, "popup");
|
|
}
|
|
else if ((popup = edgar_popup_new(gadget, gcc)))
|
|
{
|
|
evas_object_data_set(obj, "popup", popup);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/***** Gadcon IFace *******************************************************/
|
|
/*****************************************************************************/
|
|
|
|
static E_Gadcon_Client *
|
|
_edgar_gc_init(E_Gadcon *gc, const char *name, const char *id, const char *style)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
Evas_Object *obj;
|
|
const char *group;
|
|
Eina_Bool pop_on_desk = EINA_FALSE;
|
|
|
|
gadget = eina_hash_find(edgar_gadgets, name);
|
|
if (!gadget) return NULL;
|
|
|
|
DBG("EDGAR: Gadcon Init name: %s id: %s (gc orient: %d) (edjefile: %s",
|
|
name, id, gc->orient, gadget->edjefile);
|
|
|
|
// create the python Gadget class instance (if not done yet)
|
|
if (!gadget->instance)
|
|
{
|
|
DBG("EDGAR: Instantiate the python class");
|
|
gadget->instance = PyObject_CallMethod(gadget->mod, "Gadget", "");
|
|
PY_ON_ERROR_RETURN(!gadget->instance, NULL, "Cannot create the Gadget instance");
|
|
}
|
|
|
|
// do we want the popup expanded on desktop ?
|
|
if (gc->location->site == E_GADCON_SITE_DESKTOP && gadget->opt_pop_on_desk)
|
|
pop_on_desk = EINA_TRUE;
|
|
|
|
// create the edje object (popup or main)
|
|
obj = edje_object_add(gc->evas);
|
|
group = pop_on_desk ? "popup" : "main";
|
|
if (!edgar_theme_object_set(gadget, obj, group))
|
|
{
|
|
DBG("EDGAR: ERROR, cannot find a theme for the gadget: '%s'", name);
|
|
evas_object_del(obj);
|
|
return NULL;
|
|
}
|
|
|
|
// create the Gadcon_Client
|
|
E_Gadcon_Client *gcc = e_gadcon_client_new(gc, name, id, style, obj);
|
|
gcc->data = gadget;
|
|
|
|
// setup default callbacks on object (right-click for menu, left for popup)
|
|
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
|
|
edgar_mouse_down3_cb, gcc);
|
|
if (!pop_on_desk)
|
|
evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
|
|
edgar_mouse_down1_cb, gcc);
|
|
|
|
|
|
// notify the gadget about the new obj
|
|
PyObject *pyobj = object_from_instance(obj);
|
|
PyObject *ret = NULL;
|
|
if (pop_on_desk)
|
|
{
|
|
ret = PyObject_CallMethod(gadget->instance, "popup_created",
|
|
"(S)", pyobj);
|
|
PY_ON_ERROR_RETURN(!ret, NULL, "Cannot call popup_created()");
|
|
}
|
|
else
|
|
{
|
|
ret = PyObject_CallMethod(gadget->instance, "instance_created",
|
|
"(Si)", pyobj, gc->location->site);
|
|
PY_ON_ERROR_RETURN(!ret, NULL, "Cannot call instance_created()");
|
|
}
|
|
Py_XDECREF(ret);
|
|
Py_XDECREF(pyobj);
|
|
|
|
return gcc;
|
|
}
|
|
|
|
static void
|
|
_edgar_gc_shutdown(E_Gadcon_Client *gcc)
|
|
{
|
|
Edgar_Py_Gadget *gadget = gcc->data;
|
|
PyObject *pyobj, *ret;
|
|
|
|
if (!gcc || !gcc->o_base)
|
|
return;
|
|
|
|
DBG("EDGAR: Gadcon Shutdown. NAME: %s, ID: %d", gcc->name, gcc->id);
|
|
|
|
pyobj = object_from_instance(gcc->o_base);
|
|
if (gcc->gadcon->location->site == E_GADCON_SITE_DESKTOP &&
|
|
gadget->opt_pop_on_desk)
|
|
{
|
|
// call the popup_destroyed() method of the gadget.
|
|
ret = PyObject_CallMethod(gadget->instance, "popup_destroyed",
|
|
"(S)", pyobj);
|
|
PY_ON_ERROR_RETURN(!ret, , "Cannot call popup_destroyed()");
|
|
}
|
|
else
|
|
{
|
|
// call the instance_destroyed() method of the gadget.
|
|
ret = PyObject_CallMethod(gadget->instance, "instance_destroyed",
|
|
"(S)", pyobj);
|
|
PY_ON_ERROR_RETURN(!ret, , "Cannot call instance_destroyed()");
|
|
}
|
|
Py_XDECREF(ret);
|
|
Py_XDECREF(pyobj);
|
|
|
|
// destroy the object
|
|
evas_object_del(gcc->o_base);
|
|
gcc->o_base = NULL;
|
|
}
|
|
|
|
static void
|
|
_edgar_gc_orient(E_Gadcon_Client *gcc, E_Gadcon_Orient orient)
|
|
{
|
|
Edgar_Py_Gadget *gadget = gcc->data;
|
|
E_Gadcon_Orient generic;
|
|
Evas_Coord w, h, mw, mh;
|
|
Evas_Aspect_Control aspect;
|
|
|
|
DBG("EDGAR: Gadcon Orient: %d", orient);
|
|
switch (orient)
|
|
{
|
|
case E_GADCON_ORIENT_HORIZ:
|
|
case E_GADCON_ORIENT_TOP:
|
|
case E_GADCON_ORIENT_BOTTOM:
|
|
case E_GADCON_ORIENT_CORNER_TL:
|
|
case E_GADCON_ORIENT_CORNER_TR:
|
|
case E_GADCON_ORIENT_CORNER_BL:
|
|
case E_GADCON_ORIENT_CORNER_BR:
|
|
generic = E_GADCON_ORIENT_HORIZ;
|
|
break;
|
|
case E_GADCON_ORIENT_VERT:
|
|
case E_GADCON_ORIENT_LEFT:
|
|
case E_GADCON_ORIENT_RIGHT:
|
|
case E_GADCON_ORIENT_CORNER_LT:
|
|
case E_GADCON_ORIENT_CORNER_RT:
|
|
case E_GADCON_ORIENT_CORNER_LB:
|
|
case E_GADCON_ORIENT_CORNER_RB:
|
|
generic = E_GADCON_ORIENT_VERT;
|
|
break;
|
|
case E_GADCON_ORIENT_FLOAT:
|
|
default:
|
|
generic = E_GADCON_ORIENT_FLOAT;
|
|
break;
|
|
}
|
|
|
|
// call the instance_orient() method of the gadget.
|
|
PyObject *py_evas_obj = object_from_instance(gcc->o_base);
|
|
PyObject *ret = PyObject_CallMethod(gadget->instance, "instance_orient",
|
|
"(Sii)", py_evas_obj, generic, orient);
|
|
PY_ON_ERROR_RETURN(!ret, , "Cannot call instance_orient()");
|
|
Py_DECREF(py_evas_obj);
|
|
Py_DECREF(ret);
|
|
|
|
// apply obj size hints
|
|
edje_object_message_signal_process(gcc->o_base);
|
|
evas_object_size_hint_min_get(gcc->o_base, &mw, &mh);
|
|
edje_object_size_min_restricted_calc(gcc->o_base, &w, &h, mw, mh);
|
|
e_gadcon_client_min_size_set(gcc, w, h);
|
|
|
|
// also aspect hints if requested
|
|
evas_object_size_hint_aspect_get(gcc->o_base, &aspect, &w, &h);
|
|
if (aspect != EVAS_ASPECT_CONTROL_NONE)
|
|
e_gadcon_client_aspect_set(gcc, w, h);
|
|
}
|
|
|
|
static const char*
|
|
_edgar_gc_label(const E_Gadcon_Client_Class *client_class)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
|
|
DBG("EDGAR: Gadcon Label for class: %s", client_class->name);
|
|
|
|
gadget = eina_hash_find(edgar_gadgets, client_class->name);
|
|
if (!gadget) return NULL;
|
|
|
|
return gadget->label;
|
|
}
|
|
|
|
static const char*
|
|
_edgar_gc_id_new(const E_Gadcon_Client_Class *client_class)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
char buf[256];
|
|
|
|
gadget = eina_hash_find(edgar_gadgets, client_class->name);
|
|
if (!gadget) return NULL;
|
|
|
|
snprintf(buf, sizeof(buf), "ED.%s.%.0f",
|
|
gadget->name, ecore_time_get() * 1000000);
|
|
|
|
DBG("EDGAR: Gadcon ID New: %s", buf);
|
|
//TODO manage config here
|
|
return eina_stringshare_add(buf);
|
|
}
|
|
|
|
static void
|
|
_edgar_gc_id_del(const E_Gadcon_Client_Class *client_class, const char *id)
|
|
{
|
|
|
|
}
|
|
|
|
static Evas_Object*
|
|
_edgar_gc_icon(const E_Gadcon_Client_Class *client_class, Evas *evas)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
Evas_Object *icon;
|
|
|
|
DBG("EDGAR: Gadcon Icon for class: %s", client_class->name);
|
|
|
|
gadget = eina_hash_find(edgar_gadgets, client_class->name);
|
|
if (!gadget) return NULL;
|
|
|
|
icon = edje_object_add(evas);
|
|
if (edgar_theme_object_set(gadget, icon, "icon"))
|
|
return icon;
|
|
|
|
evas_object_del(icon);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/***** Gadget Python API (the python eapi module) **************************/
|
|
/*****************************************************************************/
|
|
|
|
#define EXCEPTION(_type_, _msg_) \
|
|
{ \
|
|
PyErr_SetString(_type_, _msg_); \
|
|
return NULL; \
|
|
} \
|
|
|
|
|
|
static PyObject*
|
|
_eapi_theme_object_set(PyObject *self, PyObject *args)
|
|
{
|
|
Edgar_Py_Gadget *gadget;
|
|
const char *name, *group;
|
|
Evas_Object *obj;
|
|
PyObject *pyobj;
|
|
|
|
if (!PyArg_ParseTuple(args, "Oss", &pyobj, &name, &group))
|
|
return NULL;
|
|
|
|
if (!(gadget = eina_hash_find(edgar_gadgets, name)))
|
|
EXCEPTION(PyExc_ValueError, "name is not a valid gadget name");
|
|
|
|
obj = instance_from_object(pyobj);
|
|
if (!edgar_theme_object_set(gadget, obj, group))
|
|
EXCEPTION(PyExc_RuntimeError, "cannot find group in theme");
|
|
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
|
|
static PyMethodDef EApiMethods[] = {
|
|
{"theme_object_set", _eapi_theme_object_set, METH_VARARGS,
|
|
"Load the given group from the theme file."},
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
static struct PyModuleDef EApiModule = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"eapi", /* name of module */
|
|
NULL, /* module documentation, may be NULL */
|
|
-1, /* size of per-interpreter state of the module,
|
|
or -1 if the module keeps state in global variables. */
|
|
EApiMethods
|
|
};
|
|
|
|
|
|
PyMODINIT_FUNC
|
|
PyInit_eapi(void)
|
|
{
|
|
return PyModule_Create(&EApiModule);
|
|
}
|