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