1 /*
2  * focus-manager: Single-instance managing focusable actors
3  *                for keyboard navigation
4  *
5  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA 02110-1301, USA.
21  *
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include <libxfdashboard/focus-manager.h>
30 
31 #include <glib/gi18n-lib.h>
32 
33 #include <libxfdashboard/marshal.h>
34 #include <libxfdashboard/stylable.h>
35 #include <libxfdashboard/bindings-pool.h>
36 #include <libxfdashboard/application.h>
37 #include <libxfdashboard/compat.h>
38 #include <libxfdashboard/debug.h>
39 
40 
41 /* Define this class in GObject system */
42 struct _XfdashboardFocusManagerPrivate
43 {
44 	/* Instance related */
45 	GList					*registeredFocusables;
46 	XfdashboardFocusable	*currentFocus;
47 };
48 
49 G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardFocusManager,
50 							xfdashboard_focus_manager,
51 							G_TYPE_OBJECT)
52 
53 /* Signals */
54 enum
55 {
56 	SIGNAL_REGISTERED,
57 	SIGNAL_UNREGISTERED,
58 
59 	SIGNAL_CHANGED,
60 
61 	/* Actions */
62 	ACTION_FOCUS_MOVE_FIRST,
63 	ACTION_FOCUS_MOVE_LAST,
64 	ACTION_FOCUS_MOVE_NEXT,
65 	ACTION_FOCUS_MOVE_PREVIOUS,
66 
67 	SIGNAL_LAST
68 };
69 
70 static guint XfdashboardFocusManagerSignals[SIGNAL_LAST]={ 0, };
71 
72 /* IMPLEMENTATION: Private variables and methods */
73 
74 /* Single instance of focus manager */
75 static XfdashboardFocusManager*		_xfdashboard_focus_manager=NULL;
76 
77 /* A registered focusable actor is going to be destroyed so unregister it */
_xfdashboard_focus_manager_on_focusable_destroy(XfdashboardFocusManager * self,gpointer inUserData)78 static void _xfdashboard_focus_manager_on_focusable_destroy(XfdashboardFocusManager *self,
79 															gpointer inUserData)
80 {
81 	XfdashboardFocusable			*focusable;
82 
83 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
84 	g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inUserData));
85 
86 	focusable=XFDASHBOARD_FOCUSABLE(inUserData);
87 
88 	/* Unregister going-to-be-destroyed focusable actor */
89 	xfdashboard_focus_manager_unregister(self, focusable);
90 }
91 
92 /* A registered focusable actor is going to be hidden or unrealized */
_xfdashboard_focus_manager_on_focusable_hide(XfdashboardFocusManager * self,gpointer inUserData)93 static void _xfdashboard_focus_manager_on_focusable_hide(XfdashboardFocusManager *self,
94 															gpointer inUserData)
95 {
96 	XfdashboardFocusManagerPrivate	*priv;
97 	XfdashboardFocusable			*focusable;
98 	XfdashboardFocusable			*nextFocusable;
99 
100 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
101 	g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inUserData));
102 
103 	priv=self->priv;
104 	focusable=XFDASHBOARD_FOCUSABLE(inUserData);
105 
106 	/* Only move focus if hidden or unrealized focusable actor is the one
107 	 * which has the focus currently.
108 	 */
109 	if(priv->currentFocus!=focusable) return;
110 
111 	if(clutter_actor_is_mapped(CLUTTER_ACTOR(focusable)) &&
112 		clutter_actor_is_realized(CLUTTER_ACTOR(focusable)) &&
113 		clutter_actor_is_visible(CLUTTER_ACTOR(focusable)))
114 	{
115 		return;
116 	}
117 
118 	/* Move focus to next focusable actor if this actor which has the current focus
119 	 * is going to be unrealized or hidden.
120 	 */
121 	nextFocusable=xfdashboard_focus_manager_get_next_focusable(self, priv->currentFocus);
122 	if(nextFocusable && nextFocusable!=priv->currentFocus) xfdashboard_focus_manager_set_focus(self, nextFocusable);
123 		else
124 		{
125 			xfdashboard_focusable_unset_focus(priv->currentFocus);
126 			priv->currentFocus=NULL;
127 		}
128 }
129 
130 /* Build target list of registered focusable actors for requested binding but also check
131  * if this focus manager is a target.
132  */
_xfdashboard_focus_manager_get_targets_for_binding(XfdashboardFocusManager * self,const XfdashboardBinding * inBinding)133 static GSList* _xfdashboard_focus_manager_get_targets_for_binding(XfdashboardFocusManager *self,
134 																	const XfdashboardBinding *inBinding)
135 {
136 	GSList							*targets;
137 	gboolean						mustBeFocusable;
138 	GSList							*iter;
139 	XfdashboardFocusable			*focusable;
140 
141 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
142 	g_return_val_if_fail(XFDASHBOARD_IS_BINDING(inBinding), NULL);
143 
144 	targets=NULL;
145 	mustBeFocusable=TRUE;
146 
147 	/* Get list of possible targets */
148 	targets=xfdashboard_focus_manager_get_targets(self, xfdashboard_binding_get_target(inBinding));
149 
150 	/* Determine if unfocusable targets should be included */
151 	if(xfdashboard_binding_get_flags(inBinding) & XFDASHBOARD_BINDING_FLAGS_ALLOW_UNFOCUSABLE_TARGET)
152 	{
153 		mustBeFocusable=FALSE;
154 	}
155 
156 	/* Remove unfocusable targets from list if they should not be included */
157 	if(mustBeFocusable)
158 	{
159 		for(iter=targets; iter; iter=g_slist_next(iter))
160 		{
161 			/* Get focusable actor */
162 			if(!XFDASHBOARD_IS_FOCUSABLE(iter->data)) continue;
163 			focusable=XFDASHBOARD_FOCUSABLE(iter->data);
164 
165 			/* Check if focusable actor can be focused as it may be disabled */
166 			if(!xfdashboard_focusable_can_focus(focusable))
167 			{
168 				/* Remove target from list as it cannot be focused */
169 				g_object_unref(focusable);
170 				targets=g_slist_delete_link(targets, iter);
171 			}
172 		}
173 	}
174 
175 	/* Return list of targets found */
176 	XFDASHBOARD_DEBUG(self, MISC,
177 						"Target list for action '%s' and target class '%s' has %d entries",
178 						xfdashboard_binding_get_action(inBinding),
179 						xfdashboard_binding_get_target(inBinding),
180 						g_slist_length(targets));
181 	return(targets);
182 }
183 
184 /* Action signal to move focus to first focusable actor was emitted */
_xfdashboard_focus_manager_move_focus_first(XfdashboardFocusManager * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)185 static gboolean _xfdashboard_focus_manager_move_focus_first(XfdashboardFocusManager *self,
186 															XfdashboardFocusable *inSource,
187 															const gchar *inAction,
188 															ClutterEvent *inEvent)
189 {
190 	XfdashboardFocusManagerPrivate	*priv;
191 	XfdashboardFocusable			*currentFocusable;
192 	XfdashboardFocusable			*newFocusable;
193 	GList							*iter;
194 
195 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), CLUTTER_EVENT_PROPAGATE);
196 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
197 
198 	priv=self->priv;
199 
200 	/* Get current focus */
201 	currentFocusable=xfdashboard_focus_manager_get_focus(self);
202 
203 	/* Iterate through registered focusable actor and find the first focusable one.
204 	 * We do not use xfdashboard_focus_manager_get_next_focusable(self, NULL) as
205 	 * it could return a focusable actor which is beyond the current one in order.
206 	 * We do not want to change the focus if it is not "before" the current one.
207 	 */
208 	for(iter=priv->registeredFocusables; iter; iter=g_list_next(iter))
209 	{
210 		newFocusable=(XfdashboardFocusable*)iter->data;
211 
212 		/* If iterate reached the current focused actor then there it is no first
213 		 * focusable actor and we do not need to change the focus and can return.
214 		 */
215 		if(currentFocusable && newFocusable==currentFocusable) return(CLUTTER_EVENT_STOP);
216 
217 		/* If focusable can be focused then focus it and return */
218 		if(xfdashboard_focusable_can_focus(newFocusable))
219 		{
220 			xfdashboard_focus_manager_set_focus(self, newFocusable);
221 
222 			return(CLUTTER_EVENT_STOP);
223 		}
224 	}
225 
226 	/* If we get here we iterated through all registered focusable actors but
227 	 * could not find a matching one to set focus to.
228 	 */
229 	return(CLUTTER_EVENT_STOP);
230 }
231 
232 /* Action signal to move focus to last focusable actor was emitted */
_xfdashboard_focus_manager_move_focus_last(XfdashboardFocusManager * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)233 static gboolean _xfdashboard_focus_manager_move_focus_last(XfdashboardFocusManager *self,
234 															XfdashboardFocusable *inSource,
235 															const gchar *inAction,
236 															ClutterEvent *inEvent)
237 {
238 	XfdashboardFocusManagerPrivate	*priv;
239 	XfdashboardFocusable			*currentFocusable;
240 	XfdashboardFocusable			*newFocusable;
241 	GList							*iter;
242 
243 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), CLUTTER_EVENT_PROPAGATE);
244 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
245 
246 	priv=self->priv;
247 
248 	/* Get current focus */
249 	currentFocusable=xfdashboard_focus_manager_get_focus(self);
250 
251 	/* Iterate backwards through registered focusable actor and find the last focusable
252 	 * one. We do not use xfdashboard_focus_manager_get_previous_focusable(self, NULL)
253 	 * as it could return a focusable actor which is before the current one in order.
254 	 * We do not want to change the focus if it is not "after" the current one.
255 	 */
256 	for(iter=g_list_last(priv->registeredFocusables); iter; iter=g_list_previous(iter))
257 	{
258 		newFocusable=(XfdashboardFocusable*)iter->data;
259 
260 		/* If iterate reached the current focused actor then there it is no last
261 		 * focusable actor and we do not need to change the focus and can return.
262 		 */
263 		if(currentFocusable && newFocusable==currentFocusable) return(CLUTTER_EVENT_STOP);
264 
265 		/* If focusable can be focused then focus it and return */
266 		if(xfdashboard_focusable_can_focus(newFocusable))
267 		{
268 			xfdashboard_focus_manager_set_focus(self, newFocusable);
269 
270 			return(CLUTTER_EVENT_STOP);
271 		}
272 	}
273 
274 	/* If we get here we iterated through all registered focusable actors but
275 	 * could not find a matching one to set focus to.
276 	 */
277 	return(CLUTTER_EVENT_STOP);
278 }
279 
280 /* Action signal to move focus to next focusable actor was emitted */
_xfdashboard_focus_manager_move_focus_next(XfdashboardFocusManager * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)281 static gboolean _xfdashboard_focus_manager_move_focus_next(XfdashboardFocusManager *self,
282 															XfdashboardFocusable *inSource,
283 															const gchar *inAction,
284 															ClutterEvent *inEvent)
285 {
286 	XfdashboardFocusable			*currentFocusable;
287 	XfdashboardFocusable			*newFocusable;
288 
289 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), CLUTTER_EVENT_PROPAGATE);
290 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
291 
292 	/* Get current focus */
293 	currentFocusable=xfdashboard_focus_manager_get_focus(self);
294 
295 	/* Get next focusable actor to focus */
296 	newFocusable=xfdashboard_focus_manager_get_next_focusable(self, currentFocusable);
297 	if(newFocusable) xfdashboard_focus_manager_set_focus(self, newFocusable);
298 
299 	return(CLUTTER_EVENT_STOP);
300 }
301 
302 /* Action signal to move focus to previous focusable actor was emitted */
_xfdashboard_focus_manager_move_focus_previous(XfdashboardFocusManager * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)303 static gboolean _xfdashboard_focus_manager_move_focus_previous(XfdashboardFocusManager *self,
304 																XfdashboardFocusable *inSource,
305 																const gchar *inAction,
306 																ClutterEvent *inEvent)
307 {
308 	XfdashboardFocusable			*currentFocusable;
309 	XfdashboardFocusable			*newFocusable;
310 
311 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), CLUTTER_EVENT_PROPAGATE);
312 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
313 
314 	/* Get current focus */
315 	currentFocusable=xfdashboard_focus_manager_get_focus(self);
316 
317 	/* Get next focusable actor to focus */
318 	newFocusable=xfdashboard_focus_manager_get_previous_focusable(self, currentFocusable);
319 	if(newFocusable) xfdashboard_focus_manager_set_focus(self, newFocusable);
320 
321 	return(CLUTTER_EVENT_STOP);
322 }
323 
324 /* IMPLEMENTATION: GObject */
325 
326 /* Construct this object */
_xfdashboard_focus_manager_constructor(GType inType,guint inNumberConstructParams,GObjectConstructParam * inConstructParams)327 static GObject* _xfdashboard_focus_manager_constructor(GType inType,
328 														guint inNumberConstructParams,
329 														GObjectConstructParam *inConstructParams)
330 {
331 	GObject									*object;
332 
333 	if(!_xfdashboard_focus_manager)
334 	{
335 		object=G_OBJECT_CLASS(xfdashboard_focus_manager_parent_class)->constructor(inType, inNumberConstructParams, inConstructParams);
336 		_xfdashboard_focus_manager=XFDASHBOARD_FOCUS_MANAGER(object);
337 	}
338 		else
339 		{
340 			object=g_object_ref(G_OBJECT(_xfdashboard_focus_manager));
341 		}
342 
343 	return(object);
344 }
345 
346 /* Dispose this object */
_xfdashboard_focus_manager_dispose_unregister_focusable(gpointer inData,gpointer inUserData)347 static void _xfdashboard_focus_manager_dispose_unregister_focusable(gpointer inData, gpointer inUserData)
348 {
349 	XfdashboardFocusManager			*self;
350 	XfdashboardFocusable			*focusable;
351 
352 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(inUserData));
353 	g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inData));
354 
355 	self=XFDASHBOARD_FOCUS_MANAGER(inUserData);
356 	focusable=XFDASHBOARD_FOCUSABLE(inData);
357 
358 	/* Unregister focusable actor but do not call general unregister function
359 	 * to avoid spamming focus changes and to avoid modifying list of focusable
360 	 * actor while iterating through it.
361 	 */
362 	g_signal_handlers_disconnect_by_func(focusable,
363 											G_CALLBACK(_xfdashboard_focus_manager_on_focusable_destroy),
364 											self);
365 	g_signal_handlers_disconnect_by_func(focusable,
366 											G_CALLBACK(_xfdashboard_focus_manager_on_focusable_hide),
367 											self);
368 
369 	g_signal_emit(self, XfdashboardFocusManagerSignals[SIGNAL_UNREGISTERED], 0, focusable);
370 }
371 
_xfdashboard_focus_manager_dispose(GObject * inObject)372 static void _xfdashboard_focus_manager_dispose(GObject *inObject)
373 {
374 	XfdashboardFocusManager			*self=XFDASHBOARD_FOCUS_MANAGER(inObject);
375 	XfdashboardFocusManagerPrivate	*priv=self->priv;
376 
377 	/* Release allocated resouces */
378 	if(priv->registeredFocusables)
379 	{
380 		g_list_foreach(priv->registeredFocusables, _xfdashboard_focus_manager_dispose_unregister_focusable, self);
381 		g_list_free(priv->registeredFocusables);
382 		priv->registeredFocusables=NULL;
383 	}
384 
385 	/* Call parent's class dispose method */
386 	G_OBJECT_CLASS(xfdashboard_focus_manager_parent_class)->dispose(inObject);
387 }
388 
389 /* Finalize this object */
_xfdashboard_focus_manager_finalize(GObject * inObject)390 static void _xfdashboard_focus_manager_finalize(GObject *inObject)
391 {
392 	/* Release allocated resources finally, e.g. unset singleton */
393 	if(G_LIKELY(G_OBJECT(_xfdashboard_focus_manager)==inObject))
394 	{
395 		_xfdashboard_focus_manager=NULL;
396 	}
397 
398 	/* Call parent's class dispose method */
399 	G_OBJECT_CLASS(xfdashboard_focus_manager_parent_class)->finalize(inObject);
400 }
401 
402 
403 /* Class initialization
404  * Override functions in parent classes and define properties
405  * and signals
406  */
xfdashboard_focus_manager_class_init(XfdashboardFocusManagerClass * klass)407 static void xfdashboard_focus_manager_class_init(XfdashboardFocusManagerClass *klass)
408 {
409 	GObjectClass		*gobjectClass=G_OBJECT_CLASS(klass);
410 
411 	/* Override functions */
412 	gobjectClass->constructor=_xfdashboard_focus_manager_constructor;
413 	gobjectClass->dispose=_xfdashboard_focus_manager_dispose;
414 	gobjectClass->finalize=_xfdashboard_focus_manager_finalize;
415 
416 	klass->focus_move_first=_xfdashboard_focus_manager_move_focus_first;
417 	klass->focus_move_last=_xfdashboard_focus_manager_move_focus_last;
418 	klass->focus_move_next=_xfdashboard_focus_manager_move_focus_next;
419 	klass->focus_move_previous=_xfdashboard_focus_manager_move_focus_previous;
420 
421 	/* Define signals */
422 	XfdashboardFocusManagerSignals[SIGNAL_REGISTERED]=
423 		g_signal_new("registered",
424 						G_TYPE_FROM_CLASS(klass),
425 						G_SIGNAL_RUN_LAST,
426 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, registered),
427 						NULL,
428 						NULL,
429 						g_cclosure_marshal_VOID__OBJECT,
430 						G_TYPE_NONE,
431 						1,
432 						XFDASHBOARD_TYPE_FOCUSABLE);
433 
434 	XfdashboardFocusManagerSignals[SIGNAL_UNREGISTERED]=
435 		g_signal_new("unregistered",
436 						G_TYPE_FROM_CLASS(klass),
437 						G_SIGNAL_RUN_LAST,
438 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, unregistered),
439 						NULL,
440 						NULL,
441 						g_cclosure_marshal_VOID__OBJECT,
442 						G_TYPE_NONE,
443 						1,
444 						XFDASHBOARD_TYPE_FOCUSABLE);
445 
446 	XfdashboardFocusManagerSignals[SIGNAL_CHANGED]=
447 		g_signal_new("changed",
448 						G_TYPE_FROM_CLASS(klass),
449 						G_SIGNAL_RUN_LAST,
450 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, changed),
451 						NULL,
452 						NULL,
453 						_xfdashboard_marshal_VOID__OBJECT_OBJECT,
454 						G_TYPE_NONE,
455 						2,
456 						XFDASHBOARD_TYPE_FOCUSABLE,
457 						XFDASHBOARD_TYPE_FOCUSABLE);
458 
459 	XfdashboardFocusManagerSignals[ACTION_FOCUS_MOVE_FIRST]=
460 		g_signal_new("focus-move-first",
461 						G_TYPE_FROM_CLASS(klass),
462 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
463 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, focus_move_first),
464 						g_signal_accumulator_true_handled,
465 						NULL,
466 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
467 						G_TYPE_BOOLEAN,
468 						3,
469 						XFDASHBOARD_TYPE_FOCUSABLE,
470 						G_TYPE_STRING,
471 						CLUTTER_TYPE_EVENT);
472 
473 	XfdashboardFocusManagerSignals[ACTION_FOCUS_MOVE_LAST]=
474 		g_signal_new("focus-move-last",
475 						G_TYPE_FROM_CLASS(klass),
476 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
477 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, focus_move_last),
478 						g_signal_accumulator_true_handled,
479 						NULL,
480 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
481 						G_TYPE_BOOLEAN,
482 						3,
483 						XFDASHBOARD_TYPE_FOCUSABLE,
484 						G_TYPE_STRING,
485 						CLUTTER_TYPE_EVENT);
486 
487 	XfdashboardFocusManagerSignals[ACTION_FOCUS_MOVE_NEXT]=
488 		g_signal_new("focus-move-next",
489 						G_TYPE_FROM_CLASS(klass),
490 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
491 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, focus_move_next),
492 						g_signal_accumulator_true_handled,
493 						NULL,
494 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
495 						G_TYPE_BOOLEAN,
496 						3,
497 						XFDASHBOARD_TYPE_FOCUSABLE,
498 						G_TYPE_STRING,
499 						CLUTTER_TYPE_EVENT);
500 
501 	XfdashboardFocusManagerSignals[ACTION_FOCUS_MOVE_PREVIOUS]=
502 		g_signal_new("focus-move-previous",
503 						G_TYPE_FROM_CLASS(klass),
504 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
505 						G_STRUCT_OFFSET(XfdashboardFocusManagerClass, focus_move_previous),
506 						g_signal_accumulator_true_handled,
507 						NULL,
508 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
509 						G_TYPE_BOOLEAN,
510 						3,
511 						XFDASHBOARD_TYPE_FOCUSABLE,
512 						G_TYPE_STRING,
513 						CLUTTER_TYPE_EVENT);
514 }
515 
516 /* Object initialization
517  * Create private structure and set up default values
518  */
xfdashboard_focus_manager_init(XfdashboardFocusManager * self)519 static void xfdashboard_focus_manager_init(XfdashboardFocusManager *self)
520 {
521 	XfdashboardFocusManagerPrivate	*priv;
522 
523 	priv=self->priv=xfdashboard_focus_manager_get_instance_private(self);
524 
525 	/* Set default values */
526 	priv->registeredFocusables=NULL;
527 	priv->currentFocus=NULL;
528 }
529 
530 /* IMPLEMENTATION: Public API */
531 
532 /* Get single instance of manager */
xfdashboard_focus_manager_get_default(void)533 XfdashboardFocusManager* xfdashboard_focus_manager_get_default(void)
534 {
535 	GObject									*singleton;
536 
537 	singleton=g_object_new(XFDASHBOARD_TYPE_FOCUS_MANAGER, NULL);
538 	return(XFDASHBOARD_FOCUS_MANAGER(singleton));
539 }
540 
541 /* Register a focusable actor */
xfdashboard_focus_manager_register(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable)542 void xfdashboard_focus_manager_register(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable)
543 {
544 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
545 
546 	xfdashboard_focus_manager_register_after(self, inFocusable, NULL);
547 }
548 
xfdashboard_focus_manager_register_after(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable,XfdashboardFocusable * inAfterFocusable)549 void xfdashboard_focus_manager_register_after(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable, XfdashboardFocusable *inAfterFocusable)
550 {
551 	XfdashboardFocusManagerPrivate	*priv;
552 
553 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
554 	g_return_if_fail(inFocusable);
555 	g_return_if_fail(!inAfterFocusable || XFDASHBOARD_IS_FOCUSABLE(inAfterFocusable));
556 
557 	priv=self->priv;
558 
559 	/* Check if given focusable actor is really focusable and stylable */
560 	if(!XFDASHBOARD_IS_FOCUSABLE(inFocusable))
561 	{
562 		g_warning("Object %s does not inherit %s and cannot be registered",
563 					G_OBJECT_TYPE_NAME(inFocusable),
564 					g_type_name(XFDASHBOARD_TYPE_FOCUSABLE));
565 		return;
566 	}
567 
568 	if(!XFDASHBOARD_IS_STYLABLE(inFocusable))
569 	{
570 		g_warning("Object %s does not inherit %s and cannot be registered",
571 					G_OBJECT_TYPE_NAME(inFocusable),
572 					g_type_name(XFDASHBOARD_TYPE_STYLABLE));
573 		return;
574 	}
575 
576 	/* Register focusable actor if not already registered */
577 	if(g_list_find(priv->registeredFocusables, inFocusable)==NULL)
578 	{
579 		gint						insertPosition;
580 
581 		XFDASHBOARD_DEBUG(self, MISC,
582 							"Registering focusable %s",
583 							G_OBJECT_TYPE_NAME(inFocusable));
584 
585 		/* If requested find position of focusable actor to insert new focusable actor after.
586 		 * Increase found position by one and add new focusable actor to list of registered
587 		 * focusable actors at this position. Otherwise add new focusable actor to end of list.
588 		 */
589 		insertPosition=-1;
590 		if(inAfterFocusable)
591 		{
592 			insertPosition=g_list_index(priv->registeredFocusables, inAfterFocusable);
593 			if(insertPosition!=-1) insertPosition++;
594 				else
595 				{
596 					g_warning("Could not find registered focusable object %s to register object %s - appending to end of list.",
597 								G_OBJECT_TYPE_NAME(inAfterFocusable),
598 								G_OBJECT_TYPE_NAME(inFocusable));
599 				}
600 		}
601 		priv->registeredFocusables=g_list_insert(priv->registeredFocusables, inFocusable, insertPosition);
602 
603 		/* Connect to signals to get notified if actor is going to be destroy,
604 		 * unrealized or hidden to remove it from list of focusable actors.
605 		 */
606 		g_signal_connect_swapped(inFocusable,
607 									"destroy",
608 									G_CALLBACK(_xfdashboard_focus_manager_on_focusable_destroy),
609 									self);
610 		g_signal_connect_swapped(inFocusable,
611 									"realize",
612 									G_CALLBACK(_xfdashboard_focus_manager_on_focusable_hide),
613 									self);
614 		g_signal_connect_swapped(inFocusable,
615 									"hide",
616 									G_CALLBACK(_xfdashboard_focus_manager_on_focusable_hide),
617 									self);
618 
619 		/* Emit signal */
620 		g_signal_emit(self, XfdashboardFocusManagerSignals[SIGNAL_REGISTERED], 0, inFocusable);
621 	}
622 }
623 
624 /* Unregister a focusable actor */
xfdashboard_focus_manager_unregister(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable)625 void xfdashboard_focus_manager_unregister(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable)
626 {
627 	XfdashboardFocusManagerPrivate	*priv;
628 
629 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
630 	g_return_if_fail(inFocusable);
631 
632 	priv=self->priv;
633 
634 	/* Unregister type if registered.
635 	 * We do not need to check if the given actor is focusable or stylable
636 	 * because it could not be registered if it is not.
637 	 */
638 	if(g_list_find(priv->registeredFocusables, inFocusable)!=NULL)
639 	{
640 		XFDASHBOARD_DEBUG(self, MISC,
641 							"Unregistering focusable %s",
642 							G_OBJECT_TYPE_NAME(inFocusable));
643 
644 		/* If we unregister the focusable actor which has the focus currently
645 		 * move focus to next focusable actor first but check that we will not
646 		 * reselect the focusable actor which should be unregistered. That can
647 		 * happen because this actor is not yet removed from list of registered
648 		 * focusable actor and is the only selectable one. But it needs to be
649 		 * still in the list otherwise we could not find the next actor to
650 		 * focus appropiately.
651 		 */
652 		if(inFocusable==priv->currentFocus)
653 		{
654 			XfdashboardFocusable	*focusable;
655 
656 			focusable=xfdashboard_focus_manager_get_next_focusable(self, priv->currentFocus);
657 			if(focusable && focusable!=priv->currentFocus) xfdashboard_focus_manager_set_focus(self, focusable);
658 				else
659 				{
660 					xfdashboard_focusable_unset_focus(priv->currentFocus);
661 					priv->currentFocus=NULL;
662 				}
663 		}
664 
665 		/* Remove focusable actor from list of registered focusable actors */
666 		priv->registeredFocusables=g_list_remove(priv->registeredFocusables, inFocusable);
667 
668 		/* Disconnect from signals because we are not interested in this actor anymore */
669 		g_signal_handlers_disconnect_by_func(inFocusable,
670 												G_CALLBACK(_xfdashboard_focus_manager_on_focusable_destroy),
671 												self);
672 		g_signal_handlers_disconnect_by_func(inFocusable,
673 												G_CALLBACK(_xfdashboard_focus_manager_on_focusable_hide),
674 												self);
675 
676 		/* Emit signal */
677 		g_signal_emit(self, XfdashboardFocusManagerSignals[SIGNAL_UNREGISTERED], 0, inFocusable);
678 	}
679 }
680 
681 /* Get list of registered views types.
682  * Returned GList must be freed with g_list_free() by caller.
683  */
xfdashboard_focus_manager_get_registered(XfdashboardFocusManager * self)684 GList* xfdashboard_focus_manager_get_registered(XfdashboardFocusManager *self)
685 {
686 	XfdashboardFocusManagerPrivate	*priv;
687 	GList							*copy;
688 
689 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
690 
691 	priv=self->priv;
692 
693 	/* Return a copy of list of registered view types */
694 	copy=g_list_copy(priv->registeredFocusables);
695 	return(copy);
696 }
697 
698 /* Check if given focusable actor is registered */
xfdashboard_focus_manager_is_registered(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable)699 gboolean xfdashboard_focus_manager_is_registered(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable)
700 {
701 	XfdashboardFocusManagerPrivate	*priv;
702 
703 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), FALSE);
704 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
705 
706 	priv=self->priv;
707 
708 	/* If given focusable actor is in list of registered ones return TRUE */
709 	if(g_list_find(priv->registeredFocusables, inFocusable)!=NULL) return(TRUE);
710 
711 	/* If here get here the given focusable actor is not registered */
712 	return(FALSE);
713 }
714 
715 /* Build target list of registered focusable actors for requested target class
716  * but also check if this focus manager is a target.
717  */
xfdashboard_focus_manager_get_targets(XfdashboardFocusManager * self,const gchar * inTarget)718 GSList* xfdashboard_focus_manager_get_targets(XfdashboardFocusManager *self, const gchar *inTarget)
719 {
720 	XfdashboardFocusManagerPrivate	*priv;
721 	GList							*focusablesIter;
722 	GList							*focusablesStartPoint;
723 	XfdashboardFocusable			*focusable;
724 	GType							targetType;
725 	GSList							*targets;
726 
727 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
728 	g_return_val_if_fail(inTarget && *inTarget, NULL);
729 
730 	priv=self->priv;
731 	targets=NULL;
732 
733 	/* Get type of target */
734 	targetType=g_type_from_name(inTarget);
735 	if(!targetType)
736 	{
737 		g_warning("Cannot build target list for unknown type %s", inTarget);
738 		return(NULL);
739 	}
740 
741 	/* Check if class name of requested target points to ourselve */
742 	if(g_type_is_a(G_OBJECT_TYPE(self), targetType))
743 	{
744 		targets=g_slist_append(targets, g_object_ref(self));
745 	}
746 
747 	/* Check if class name of requested target points to application */
748 	if(g_type_is_a(XFDASHBOARD_TYPE_APPLICATION, targetType))
749 	{
750 		targets=g_slist_append(targets, g_object_ref(xfdashboard_application_get_default()));
751 	}
752 
753 	/* Iterate through list of registered actors and add each one
754 	 * matching the target class name to the list of targets.
755 	 * Begin with finding starting point of iteration.
756 	 */
757 	focusablesStartPoint=g_list_find(priv->registeredFocusables, priv->currentFocus);
758 	if(!focusablesStartPoint) focusablesStartPoint=priv->registeredFocusables;
759 
760 	/* Iterate through list of registered actors beginning at found starting
761 	 * point of iteration (might be begin of list of registered actors)
762 	 * and add each actor matching target class name to target list.
763 	 */
764 	for(focusablesIter=focusablesStartPoint; focusablesIter; focusablesIter=g_list_next(focusablesIter))
765 	{
766 		focusable=(XfdashboardFocusable*)focusablesIter->data;
767 
768 		/* If focusable can be focused and matches target class name
769 		 * then add it to target list.
770 		 */
771 		if(g_type_is_a(G_OBJECT_TYPE(focusable), targetType))
772 		{
773 			targets=g_slist_append(targets, g_object_ref(focusable));
774 		}
775 	}
776 
777 	/* We have to continue search at the beginning of list of registered actors
778 	 * up to the found starting point of iteration. Add each actor matching
779 	 * target class name to target list.
780 	 */
781 	for(focusablesIter=priv->registeredFocusables; focusablesIter!=focusablesStartPoint; focusablesIter=g_list_next(focusablesIter))
782 	{
783 		focusable=(XfdashboardFocusable*)focusablesIter->data;
784 
785 		/* If focusable can be focused and matches target class name
786 		 * then add it to target list.
787 		 */
788 		if(g_type_is_a(G_OBJECT_TYPE(focusable), targetType))
789 		{
790 			targets=g_slist_append(targets, g_object_ref(focusable));
791 		}
792 	}
793 
794 	/* Return list of targets found */
795 	XFDASHBOARD_DEBUG(self, MISC,
796 						"Target list for target class '%s' has %d entries",
797 						inTarget,
798 						g_slist_length(targets));
799 
800 	return(targets);
801 }
802 
803 /* Determine if a specific actor has the focus */
xfdashboard_focus_manager_has_focus(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable)804 gboolean xfdashboard_focus_manager_has_focus(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable)
805 {
806 	XfdashboardFocusManagerPrivate	*priv;
807 
808 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), FALSE);
809 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
810 
811 	priv=self->priv;
812 
813 	/* Return TRUE if given actor has the focus otherwise return FALSE */
814 	return(priv->currentFocus==inFocusable ? TRUE : FALSE);
815 }
816 
817 /* Get focusable actor which has the focus currently */
xfdashboard_focus_manager_get_focus(XfdashboardFocusManager * self)818 XfdashboardFocusable* xfdashboard_focus_manager_get_focus(XfdashboardFocusManager *self)
819 {
820 	XfdashboardFocusManagerPrivate	*priv;
821 
822 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
823 
824 	priv=self->priv;
825 
826 	/* Return found focused focusable actor */
827 	return(priv->currentFocus);
828 }
829 
830 /* Set focus to a registered focusable actor */
xfdashboard_focus_manager_set_focus(XfdashboardFocusManager * self,XfdashboardFocusable * inFocusable)831 void xfdashboard_focus_manager_set_focus(XfdashboardFocusManager *self, XfdashboardFocusable *inFocusable)
832 {
833 	XfdashboardFocusManagerPrivate	*priv;
834 	XfdashboardFocusable			*oldFocusable;
835 
836 	g_return_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self));
837 	g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable));
838 
839 	priv=self->priv;
840 	oldFocusable=NULL;
841 
842 	/* Check if focusable actor is really registered */
843 	if(g_list_find(priv->registeredFocusables, inFocusable)==NULL)
844 	{
845 		g_warning("Trying to focus an unregistered focusable actor");
846 		return;
847 	}
848 
849 	/* Check if new focusable actor can be focussed. If it cannot be focussed
850 	 * move focus to next focusable actor. If no focusable actor can be found
851 	 * do not change focus at all.
852 	 */
853 	if(!xfdashboard_focusable_can_focus(inFocusable))
854 	{
855 		XfdashboardFocusable		*newFocusable;
856 
857 		newFocusable=xfdashboard_focus_manager_get_next_focusable(self, inFocusable);
858 		if(!newFocusable)
859 		{
860 			XFDASHBOARD_DEBUG(self, MISC,
861 								"Requested focusable actor '%s' cannot be focus but no other focusable actor was found",
862 								G_OBJECT_TYPE_NAME(inFocusable));
863 			return;
864 		}
865 
866 		XFDASHBOARD_DEBUG(self, MISC,
867 							"Requested focusable actor '%s' cannot be focused - moving focus to '%s'",
868 							G_OBJECT_TYPE_NAME(inFocusable),
869 							newFocusable ? G_OBJECT_TYPE_NAME(newFocusable) : "<nothing>");
870 		inFocusable=newFocusable;
871 	}
872 
873 	/* Do nothing if current focused actor and new one are the same */
874 	oldFocusable=priv->currentFocus;
875 	if(oldFocusable==inFocusable)
876 	{
877 		XFDASHBOARD_DEBUG(self, MISC, "Current focused actor and new one are the same so do nothing.");
878 		return;
879 	}
880 
881 	/* Unset focus at current focused actor */
882 	if(priv->currentFocus)
883 	{
884 		xfdashboard_focusable_unset_focus(priv->currentFocus);
885 		priv->currentFocus=NULL;
886 	}
887 
888 	/* Set focus to new focusable actor */
889 	priv->currentFocus=inFocusable;
890 	xfdashboard_focusable_set_focus(priv->currentFocus);
891 	XFDASHBOARD_DEBUG(self, MISC,
892 						"Moved focus from '%s' to '%s'",
893 						oldFocusable ? G_OBJECT_TYPE_NAME(oldFocusable) : "<nothing>",
894 						G_OBJECT_TYPE_NAME(priv->currentFocus));
895 
896 	/* Emit signal for changed focus */
897 	g_signal_emit(self, XfdashboardFocusManagerSignals[SIGNAL_CHANGED], 0, oldFocusable, priv->currentFocus);
898 }
899 
900 /* Find next focusable actor from given focusable actor */
xfdashboard_focus_manager_get_next_focusable(XfdashboardFocusManager * self,XfdashboardFocusable * inBeginFocusable)901 XfdashboardFocusable* xfdashboard_focus_manager_get_next_focusable(XfdashboardFocusManager *self,
902 																	XfdashboardFocusable *inBeginFocusable)
903 {
904 	XfdashboardFocusManagerPrivate	*priv;
905 	GList							*startIteration;
906 	GList							*iter;
907 	XfdashboardFocusable			*focusable;
908 
909 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
910 	g_return_val_if_fail(!inBeginFocusable || XFDASHBOARD_IS_FOCUSABLE(inBeginFocusable), NULL);
911 
912 	priv=self->priv;
913 	startIteration=NULL;
914 
915 	/* Find starting point of iteration.
916 	 * If starting focusable actor for search for next focusable actor is NULL
917 	 * or if it is not registered start search at begin of list of focusable actors.
918 	 */
919 	if(inBeginFocusable) startIteration=g_list_find(priv->registeredFocusables, inBeginFocusable);
920 	if(startIteration) startIteration=g_list_next(startIteration);
921 		else startIteration=priv->registeredFocusables;
922 
923 	/* Iterate through list of registered focusable actors beginning at
924 	 * given focusable actor (might be begin of this list) and return
925 	 * the first focusable actor which can be focused.
926 	 */
927 	for(iter=startIteration; iter; iter=g_list_next(iter))
928 	{
929 		focusable=(XfdashboardFocusable*)iter->data;
930 
931 		/* If focusable can be focused then return it */
932 		if(xfdashboard_focusable_can_focus(focusable)) return(focusable);
933 	}
934 
935 	/* If we get here we have to continue search at the beginning of list
936 	 * of registered focusable actors. Iterate through list of registered
937 	 * focusable actors from the beginning of that list up to the given
938 	 * focusable actor and return the first focusable actor which is focusable.
939 	 */
940 	for(iter=priv->registeredFocusables; iter!=startIteration; iter=g_list_next(iter))
941 	{
942 		focusable=(XfdashboardFocusable*)iter->data;
943 
944 		/* If focusable can be focused then return it */
945 		if(xfdashboard_focusable_can_focus(focusable)) return(focusable);
946 	}
947 
948 	/* If we get here we could not find next focusable actor */
949 	return(NULL);
950 }
951 
952 /* Find previous focusable actor from given focusable actor */
xfdashboard_focus_manager_get_previous_focusable(XfdashboardFocusManager * self,XfdashboardFocusable * inBeginFocusable)953 XfdashboardFocusable* xfdashboard_focus_manager_get_previous_focusable(XfdashboardFocusManager *self,
954 																		XfdashboardFocusable *inBeginFocusable)
955 {
956 	XfdashboardFocusManagerPrivate	*priv;
957 	GList							*startIteration;
958 	GList							*iter;
959 	XfdashboardFocusable			*focusable;
960 
961 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), NULL);
962 	g_return_val_if_fail(!inBeginFocusable || XFDASHBOARD_IS_FOCUSABLE(inBeginFocusable), NULL);
963 
964 	priv=self->priv;
965 	startIteration=NULL;
966 
967 	/* Find starting point of iteration.
968 	 * If starting focusable actor for search for next focusable actor is NULL
969 	 * or if it is not registered start search at begin of list of focusable actors.
970 	 */
971 	if(inBeginFocusable) startIteration=g_list_find(priv->registeredFocusables, inBeginFocusable);
972 	if(startIteration) startIteration=g_list_previous(startIteration);
973 		else startIteration=priv->registeredFocusables;
974 
975 	/* Iterate reverse through list of registered focusable actors beginning
976 	 * at given focusable actor (might be begin of this list) and return
977 	 * the first focusable actor which can be focused.
978 	 */
979 	for(iter=startIteration; iter; iter=g_list_previous(iter))
980 	{
981 		focusable=(XfdashboardFocusable*)iter->data;
982 
983 		/* If focusable can be focused then return it */
984 		if(xfdashboard_focusable_can_focus(focusable)) return(focusable);
985 	}
986 
987 	/* If we get here we have to continue search at the end of list
988 	 * of registered focusable actors. Iterate reverse through list of
989 	 * registered focusable actors from the beginning of that list up
990 	 * to the given focusable actor and return the first focusable actor
991 	 * which is focusable.
992 	 */
993 	for(iter=g_list_last(priv->registeredFocusables); iter!=startIteration; iter=g_list_previous(iter))
994 	{
995 		focusable=(XfdashboardFocusable*)iter->data;
996 
997 		/* If focusable can be focused then return it */
998 		if(xfdashboard_focusable_can_focus(focusable)) return(focusable);
999 	}
1000 
1001 	/* If we get here we could not find next focusable actor */
1002 	return(NULL);
1003 }
1004 
1005 /* Determine list of target actors and the action to perform for key-press or
1006  * key-release event.
1007  */
xfdashboard_focus_manager_get_event_targets_and_action(XfdashboardFocusManager * self,const ClutterEvent * inEvent,XfdashboardFocusable * inFocusable,GSList ** outTargets,const gchar ** outAction)1008 gboolean xfdashboard_focus_manager_get_event_targets_and_action(XfdashboardFocusManager *self,
1009 																const ClutterEvent *inEvent,
1010 																XfdashboardFocusable *inFocusable,
1011 																GSList **outTargets,
1012 																const gchar **outAction)
1013 {
1014 	XfdashboardFocusManagerPrivate	*priv;
1015 	XfdashboardBindingsPool			*bindings;
1016 	const XfdashboardBinding		*binding;
1017 	const gchar						*action;
1018 	GSList							*targetFocusables;
1019 	gboolean						status;
1020 
1021 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), FALSE);
1022 	g_return_val_if_fail(inEvent, FALSE);
1023 	g_return_val_if_fail(clutter_event_type(inEvent)==CLUTTER_KEY_PRESS || clutter_event_type(inEvent)==CLUTTER_KEY_RELEASE, FALSE);
1024 	g_return_val_if_fail(!inFocusable || XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
1025 	g_return_val_if_fail(outTargets && *outTargets==NULL, FALSE);
1026 	g_return_val_if_fail(outAction && *outAction==NULL, FALSE);
1027 
1028 	priv=self->priv;
1029 	action=NULL;
1030 	targetFocusables=NULL;
1031 	status=FALSE;
1032 
1033 	/* If no focusable actor was specified then use current focused actor */
1034 	if(!inFocusable)
1035 	{
1036 		inFocusable=priv->currentFocus;
1037 
1038 		/* If still no focusable actor is available we cannot handle event
1039 		 * so let the others try it by propagating event.
1040 		 */
1041 		if(!inFocusable) return(FALSE);
1042 	}
1043 
1044 	/* Take reference on ourselve and the focusable actor to keep them alive when handling event */
1045 	g_object_ref(self);
1046 	g_object_ref(inFocusable);
1047 
1048 	/* Lookup action for event and emit action if a binding was found
1049 	 * for this event.
1050 	 */
1051 	bindings=xfdashboard_bindings_pool_get_default();
1052 	binding=xfdashboard_bindings_pool_find_for_event(bindings, CLUTTER_ACTOR(inFocusable), inEvent);
1053 	if(binding)
1054 	{
1055 		const gchar					*target;
1056 
1057 		/* Get action of binding */
1058 		action=xfdashboard_binding_get_action(binding);
1059 
1060 		/* Build up list of targets which is either the requested focusable actor,
1061 		 * the current focused actor or focusable actors of a specific type
1062 		 */
1063 		targetFocusables=NULL;
1064 		target=xfdashboard_binding_get_target(binding);
1065 		if(target)
1066 		{
1067 			/* Target class name is specified so build up a list of targets */
1068 			targetFocusables=_xfdashboard_focus_manager_get_targets_for_binding(self, binding);
1069 		}
1070 			else
1071 			{
1072 				/* No target class name was specified so add requested focusable
1073 				 * actor to list of target.
1074 				 */
1075 				targetFocusables=g_slist_append(targetFocusables, g_object_ref(inFocusable));
1076 			}
1077 
1078 		/* If target list is not empty then this event can be handled and status
1079 		 * can to set to TRUE to reflect this state. Otherwise release allocated
1080 		 * resources to prevent returning them to callee.
1081 		 */
1082 		if(g_slist_length(targetFocusables)>0) status=TRUE;
1083 			else
1084 			{
1085 				/* Release allocated resources */
1086 				if(targetFocusables)
1087 				{
1088 					g_slist_free_full(targetFocusables, g_object_unref);
1089 					targetFocusables=NULL;
1090 				}
1091 
1092 				if(action)
1093 				{
1094 					action=NULL;
1095 				}
1096 			}
1097 	}
1098 	g_object_unref(bindings);
1099 
1100 	/* Release reference on ourselve and the focusable actor to took to keep them alive  */
1101 	g_object_unref(inFocusable);
1102 	g_object_unref(self);
1103 
1104 	/* Store result at pointers if given otherwise release allocated resources */
1105 	if(outTargets) *outTargets=targetFocusables;
1106 		else g_slist_free_full(targetFocusables, g_object_unref);
1107 
1108 	if(outAction) *outAction=action;
1109 
1110 	/* Return status result */
1111 	return(status);
1112 }
1113 
1114 /* Handle key event (it is either key-press or key-release) by focusable actor
1115  * which has the focus or by specified actor.
1116  */
xfdashboard_focus_manager_handle_key_event(XfdashboardFocusManager * self,const ClutterEvent * inEvent,XfdashboardFocusable * inFocusable)1117 gboolean xfdashboard_focus_manager_handle_key_event(XfdashboardFocusManager *self,
1118 													const ClutterEvent *inEvent,
1119 													XfdashboardFocusable *inFocusable)
1120 {
1121 	XfdashboardFocusManagerPrivate	*priv;
1122 	GSList							*targetFocusables;
1123 	const gchar						*action;
1124 
1125 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUS_MANAGER(self), CLUTTER_EVENT_PROPAGATE);
1126 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
1127 	g_return_val_if_fail(clutter_event_type(inEvent)==CLUTTER_KEY_PRESS || clutter_event_type(inEvent)==CLUTTER_KEY_RELEASE, CLUTTER_EVENT_PROPAGATE);
1128 	g_return_val_if_fail(!inFocusable || XFDASHBOARD_IS_FOCUSABLE(inFocusable), CLUTTER_EVENT_PROPAGATE);
1129 
1130 	priv=self->priv;
1131 
1132 	/* If no focusable actor was specified then use current focused actor */
1133 	if(!inFocusable)
1134 	{
1135 		inFocusable=priv->currentFocus;
1136 
1137 		/* If still no focusable actor is available we cannot handle event
1138 		 * so let the others try it by propagating event.
1139 		 */
1140 		if(!inFocusable) return(CLUTTER_EVENT_PROPAGATE);
1141 	}
1142 
1143 	/* Get targets and action for this event and synthesize event for specified
1144 	 * focusable actor
1145 	 */
1146 	targetFocusables=NULL;
1147 	action=NULL;
1148 	if(xfdashboard_focus_manager_get_event_targets_and_action(self, inEvent, inFocusable, &targetFocusables, &action))
1149 	{
1150 		gboolean				eventStatus;
1151 		GSList					*iter;
1152 		GSignalQuery			signalData={ 0, };
1153 
1154 		eventStatus=CLUTTER_EVENT_PROPAGATE;
1155 
1156 		/* Emit action of binding to each actor in target list just build up */
1157 		XFDASHBOARD_DEBUG(self, MISC,
1158 							"Target list for action '%s' has %d actors",
1159 							action,
1160 							g_slist_length(targetFocusables));
1161 
1162 		for(iter=targetFocusables; iter; iter=g_slist_next(iter))
1163 		{
1164 			GObject				*targetObject;
1165 			guint				signalID;
1166 
1167 			/* Get target to emit action signal at */
1168 			targetObject=G_OBJECT(iter->data);
1169 
1170 			/* Check if target provides action requested as signal */
1171 			signalID=g_signal_lookup(action, G_OBJECT_TYPE(targetObject));
1172 			if(!signalID)
1173 			{
1174 				g_warning("Object type %s does not provide action '%s'",
1175 							G_OBJECT_TYPE_NAME(targetObject),
1176 							action);
1177 				continue;
1178 			}
1179 
1180 			/* Query signal for detailed data */
1181 			g_signal_query(signalID, &signalData);
1182 
1183 			/* Check if signal is an action signal */
1184 			if(!(signalData.signal_flags & G_SIGNAL_ACTION))
1185 			{
1186 				g_warning("Action '%s' at object type %s is not an action signal.",
1187 							action,
1188 							G_OBJECT_TYPE_NAME(targetObject));
1189 				continue;
1190 			}
1191 
1192 #if DEBUG
1193 			/* In debug mode also check if signal has right signature
1194 			 * to be able to handle this action properly.
1195 			 */
1196 			if(signalID)
1197 			{
1198 				GType				returnValueType=G_TYPE_BOOLEAN;
1199 				GType				parameterTypes[]={ XFDASHBOARD_TYPE_FOCUSABLE, G_TYPE_STRING, CLUTTER_TYPE_EVENT };
1200 				guint				parameterCount;
1201 				guint				i;
1202 
1203 				/* Check if signal wants the right type of return value */
1204 				if(signalData.return_type!=returnValueType)
1205 				{
1206 					g_critical("Action '%s' at object type %s wants return value of type %s but expected is %s.",
1207 								action,
1208 								G_OBJECT_TYPE_NAME(targetObject),
1209 								g_type_name(signalData.return_type),
1210 								g_type_name(returnValueType));
1211 				}
1212 
1213 				/* Check if signals wants the right number and types of parameters */
1214 				parameterCount=sizeof(parameterTypes)/sizeof(GType);
1215 				if(signalData.n_params!=parameterCount)
1216 				{
1217 					g_critical("Action '%s' at object type %s wants %u parameters but expected are %u.",
1218 								action,
1219 								G_OBJECT_TYPE_NAME(targetObject),
1220 								signalData.n_params,
1221 								parameterCount);
1222 				}
1223 
1224 				for(i=0; i<(parameterCount<signalData.n_params ? parameterCount : signalData.n_params); i++)
1225 				{
1226 					if(signalData.param_types[i]!=parameterTypes[i])
1227 					{
1228 						g_critical("Action '%s' at object type %s wants type %s at parameter %u but type %s is expected.",
1229 									action,
1230 									G_OBJECT_TYPE_NAME(targetObject),
1231 									g_type_name(signalData.param_types[i]),
1232 									i+1,
1233 									g_type_name(parameterTypes[i]));
1234 					}
1235 				}
1236 			}
1237 #endif
1238 
1239 			/* Emit action signal at target */
1240 			XFDASHBOARD_DEBUG(self, ACTOR,
1241 								"Emitting action signal '%s' at focusable actor %s",
1242 								action,
1243 								G_OBJECT_TYPE_NAME(targetObject));
1244 			g_signal_emit_by_name(targetObject, action, inFocusable, action, inEvent, &eventStatus);
1245 			XFDASHBOARD_DEBUG(self, ACTOR,
1246 								"Action signal '%s' was %s by focusable actor %s",
1247 								action,
1248 								eventStatus==CLUTTER_EVENT_STOP ? "handled" : "not handled",
1249 								G_OBJECT_TYPE_NAME(targetObject));
1250 		}
1251 
1252 		/* Release allocated resources */
1253 		g_slist_free_full(targetFocusables, g_object_unref);
1254 
1255 		if(eventStatus==CLUTTER_EVENT_STOP) return(CLUTTER_EVENT_STOP);
1256 	}
1257 
1258 	/* Event was not handled so synthesize event to specified focusable actor */
1259 	return(clutter_actor_event(CLUTTER_ACTOR(inFocusable), inEvent, FALSE));
1260 }
1261