Merge "custom eail widget implementation" into tizen
[platform/core/uifw/eail.git] / eail / eail.c
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 /**
21  * @file eail.c
22  * @brief EAIL initialization
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <Elementary.h>
30
31 #include <gmodule.h>
32 #include <atk-bridge.h>
33 #include "eail.h"
34 #include "eail_app.h"
35 #include "eail_priv.h"
36 #include "eail_clipboard.h"
37
38 /** @brief Struct definition for listener info*/
39 typedef struct _EailUtilListenerInfo EailUtilListenerInfo;
40 /** @brief Struct definition for event info*/
41 typedef struct _EailKeyEventInfo EailKeyEventInfo;
42
43 /** @brief Struct definition for listener info*/
44 struct _EailUtilListenerInfo
45 {
46    gint key; /**< @brief key of entry */
47    guint signal_id;/**< @brief id of the signal */
48    gulong hook_id;/**< @brief emit hook value returned on signal registration*/
49 };
50
51 /** @brief Struct definition for event info*/
52 struct _EailKeyEventInfo
53 {
54   AtkKeyEventStruct *key_event;/**< @brief key of event */
55   gpointer func_data;/**< @brief additional data passed to the event */
56 };
57
58 /** @brief Cache of objects used for listening and propagating signals */
59 static GHashTable *listener_list = NULL;
60 /** @brief Last added id of a listener */
61 static gint listener_idx = 1;
62
63 /**
64  * @brief Domain index that will be used as the DOMAIN parameter on EINA log macros
65  *
66  * A negative value means a log occurred.
67  */
68 int _eail_log_dom = -1;
69
70 /**
71  * @brief A variable for tracking the last focused AtkObject
72  */
73 static AtkObject *eail_atk_last_focused_obj = NULL;
74
75 /**
76  * @brief Gets the name of the toolkit
77  * @return string representing the name of the toolkit
78  */
79 static const gchar * eail_get_toolkit_name(void)
80 {
81    return "EAIL";
82 }
83
84 /**
85  * @brief Gets the version of the toolkit
86  *
87  * @return string representing the version of the toolkit
88  */
89 static const gchar * eail_get_toolkit_version(void)
90 {
91    return g_strdup(PACKAGE_VERSION);
92 }
93
94 /**
95  * @brief Gets the accessible root container for the current application
96  *
97  * @return AtkObject representing the accessible root container
98  */
99 static AtkObject * eail_get_root(void)
100 {
101    static AtkObject *root = NULL;
102
103    if (!root)
104      {
105         root = g_object_new(EAIL_TYPE_APP, NULL);
106         atk_object_initialize(root, NULL);
107    }
108
109    return root;
110 }
111
112 /**
113  * @brief Callback to be called when an object receives focus
114  *
115  * @param current_focused_obj AtkObject instance
116  */
117 static void
118 eail_focus_listener_cb(AtkObject *current_focused_obj)
119 {
120    if (current_focused_obj == eail_atk_last_focused_obj)
121      return;
122
123    if (eail_atk_last_focused_obj)
124      {
125         atk_object_notify_state_change
126                     (eail_atk_last_focused_obj, ATK_STATE_FOCUSED, FALSE);
127         g_object_unref(eail_atk_last_focused_obj);
128      }
129
130    g_object_ref(current_focused_obj);
131
132    /* already notifying about focus in widget implementation so do not need
133     * to notify here for new focus*/
134    eail_atk_last_focused_obj = current_focused_obj;
135 }
136
137 /**
138  * @brief Initializes object focus tracking
139  */
140 static void
141 eail_app_focus_listener_init()
142 {
143    atk_add_focus_tracker(eail_focus_listener_cb);
144 }
145
146 /**
147  * @brief Creates and adds a listener for the given object type
148  *
149  * @param listener GObject Emission Hook (a simple function pointer to get
150  * invoked when the signal is emitted)
151  * @param object_type name string representing object's type
152  * @param signal name string representing the signal to listen
153  * @param hook_data GObject 'hook' info
154  *
155  * @returns integer representing the id of the newly added listener
156  */
157 static guint
158 add_listener (GSignalEmissionHook listener,
159               const gchar         *object_type,
160               const gchar         *signal,
161               const gchar         *hook_data)
162 {
163   GType type;
164   guint signal_id;
165   gint  rc = 0;
166
167   type = g_type_from_name (object_type);
168   if (type)
169     {
170       signal_id  = g_signal_lookup (signal, type);
171       if (signal_id > 0)
172         {
173           EailUtilListenerInfo *listener_info;
174
175           rc = listener_idx;
176
177           listener_info = g_malloc(sizeof(EailUtilListenerInfo));
178           listener_info->key = listener_idx;
179           listener_info->hook_id =
180                           g_signal_add_emission_hook (signal_id, 0, listener,
181                                                       g_strdup (hook_data),
182                                                       (GDestroyNotify) g_free);
183           listener_info->signal_id = signal_id;
184
185           g_hash_table_insert(listener_list, &(listener_info->key), listener_info);
186           listener_idx++;
187         }
188       else
189         {
190            DBG("Invalid signal type %s\n", signal);
191         }
192     }
193   else
194     {
195        DBG("Invalid object type %s\n", object_type);
196     }
197   return rc;
198 }
199
200 /**
201  * @brief Removes a listener for given object type
202  *
203  * @param remove_listener id of the listener to remove
204  */
205 static void
206 eail_remove_global_event_listener (guint remove_listener)
207 {
208   if (remove_listener > 0)
209   {
210     EailUtilListenerInfo *listener_info;
211     gint tmp_idx = remove_listener;
212
213     listener_info = (EailUtilListenerInfo *)
214                                  g_hash_table_lookup(listener_list, &tmp_idx);
215
216     if (listener_info != NULL)
217       {
218         /* Hook id of 0 and signal id of 0 are invalid */
219         if (listener_info->hook_id != 0 && listener_info->signal_id != 0)
220           {
221             /* Remove the emission hook */
222             g_signal_remove_emission_hook(listener_info->signal_id,
223                                                 listener_info->hook_id);
224
225             /* Remove the element from the hash */
226             g_hash_table_remove(listener_list, &tmp_idx);
227           }
228         else
229           {
230              DBG("Invalid listener hook_id %ld or signal_id %d\n",
231                             listener_info->hook_id, listener_info->signal_id);
232           }
233       }
234     else
235       {
236          DBG("No listener with the specified listener id %d", remove_listener);
237       }
238   }
239   else
240   {
241      DBG("Invalid listener_id %d", remove_listener);
242   }
243 }
244
245 /**
246  * @brief Initialization for global event listener
247  *
248  * @param listener GSignalEmissionHook (GObject 'signal invocation hint')
249  * @param event_type string representing the event's type
250  *
251  * @return integer representing the id of the added listener
252  */
253 static guint
254 eail_add_global_event_listener(GSignalEmissionHook listener,
255                                const gchar *event_type)
256 {
257    guint rc = 0;
258    gchar **split_string;
259
260    split_string = g_strsplit(event_type, ":", 3);
261
262    if (split_string)
263      {
264        if (!strcmp("window", split_string[0]))
265          rc = add_listener /* window event handling */
266                        (listener, "AtkWindow", split_string[1], event_type);
267        else
268          rc = add_listener /* regular event handling */
269                     (listener, split_string[1], split_string[2], event_type);
270
271        g_strfreev(split_string);
272      }
273
274    return rc;
275 }
276
277 /**
278  * @brief Destructor for listener info object
279  *
280  * @param data data to be freed
281  */
282 static void
283 eail_listener_info_destroy(gpointer data)
284 {
285    g_free(data);
286 }
287
288 /**
289  * @brief AtkUtil class initialization
290  */
291 static void atk_util_install(void)
292 {
293    AtkUtilClass *uclass;
294
295    uclass = ATK_UTIL_CLASS(g_type_class_ref(ATK_TYPE_UTIL));
296    uclass->get_toolkit_name = eail_get_toolkit_name;
297    uclass->get_toolkit_version = eail_get_toolkit_version;
298    uclass->get_root = eail_get_root;
299
300    eail_app_focus_listener_init();
301
302    uclass->add_global_event_listener = eail_add_global_event_listener;
303    uclass->remove_global_event_listener = eail_remove_global_event_listener;
304    uclass->add_key_event_listener = NULL;
305    uclass->remove_key_event_listener = NULL;
306 }
307
308 /**
309  * @brief Function to be executed by Elementary when EAIL module is loaded
310  *
311  * @param m Elm_Module data
312  * @return 1 on success, 0 otherwise
313  */
314 int
315 elm_modapi_init(void *m)
316 {
317    static gboolean initialized = FALSE;
318    if (initialized) return 1;
319
320    _eail_log_dom = eina_log_domain_register("eail", EAIL_LOG_COLOR);
321    if (!_eail_log_dom)
322      {
323         EINA_LOG_ERR("could not register eail log domain.");
324         _eail_log_dom = EINA_LOG_DOMAIN_GLOBAL;
325      }
326
327    if (!ecore_main_loop_glib_integrate())
328      {
329         ERR("Cannot integrate with glib main loop");
330         return 0;
331      }
332
333    initialized = TRUE;
334 #if !GLIB_CHECK_VERSION(2,35,0)
335    g_type_init();
336 #endif
337    listener_list = g_hash_table_new_full
338                   (g_int_hash, g_int_equal, NULL, eail_listener_info_destroy);
339
340    atk_util_install();
341
342    atk_misc_instance = g_object_new(ATK_TYPE_MISC, NULL);
343
344    atk_bridge_adaptor_init(NULL, NULL);
345
346    return 1;
347 }
348
349 /**
350  * @brief Function to be executed by Elementary when EAIL module is unloaded
351  *
352  * @param m Elm_Module data
353  * @return always 1 - notifying success
354  */
355 int
356 elm_modapi_shutdown(void *m)
357 {
358    eail_clipboard_free();
359
360    if ((_eail_log_dom > -1) && (_eail_log_dom != EINA_LOG_DOMAIN_GLOBAL))
361      {
362         eina_log_domain_unregister(_eail_log_dom);
363         _eail_log_dom = -1;
364      }
365    /*always succeed*/
366    return 1;
367 }
368