1 /*
2 * popup-menu: A pop-up menu with menu items performing an action when an menu
3 * item was clicked
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 /**
26 * SECTION:popup-menu
27 * @short_description: A pop-up menu showing items and perfoming an action
28 when an item was clicked
29 * @include: xfdashboard/popup-menu.h
30 *
31 * A #XfdashboardPopupMenu implements a drop down menu consisting of a list of
32 * #ClutterActor objects as menu items which can be navigated and activated by
33 * the user to perform the associated action of the selected menu item.
34 *
35 * The following example shows how create and activate a #XfdashboardPopupMenu
36 * when an actor was clicked.
37 * application when clicked:
38 *
39 * |[<!-- language="C" -->
40 * ]|
41 */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <libxfdashboard/popup-menu.h>
48
49 #include <glib/gi18n-lib.h>
50 #include <gtk/gtk.h>
51 #include <gdk/gdk.h>
52 #include <math.h>
53
54 #include <libxfdashboard/box-layout.h>
55 #include <libxfdashboard/focusable.h>
56 #include <libxfdashboard/focus-manager.h>
57 #include <libxfdashboard/stylable.h>
58 #include <libxfdashboard/window-tracker.h>
59 #include <libxfdashboard/application.h>
60 #include <libxfdashboard/click-action.h>
61 #include <libxfdashboard/bindings-pool.h>
62 #include <libxfdashboard/button.h>
63 #include <libxfdashboard/enums.h>
64 #include <libxfdashboard/utils.h>
65 #include <libxfdashboard/compat.h>
66 #include <libxfdashboard/debug.h>
67
68
69 /* Define this class in GObject system */
70 static void _xfdashboard_popup_menu_focusable_iface_init(XfdashboardFocusableInterface *iface);
71
72 struct _XfdashboardPopupMenuPrivate
73 {
74 /* Properties related */
75 gboolean destroyOnCancel;
76
77 ClutterActor *source;
78
79 gboolean showTitle;
80 gboolean showTitleIcon;
81
82 /* Instance related */
83 gboolean isActive;
84
85 ClutterActor *title;
86 ClutterActor *itemsContainer;
87
88 XfdashboardWindowTracker *windowTracker;
89
90 XfdashboardFocusManager *focusManager;
91 gpointer oldFocusable;
92 gpointer selectedItem;
93
94 XfdashboardStage *stage;
95 guint capturedEventSignalID;
96
97 guint sourceDestroySignalID;
98
99 guint suspendSignalID;
100 };
101
102 G_DEFINE_TYPE_WITH_CODE(XfdashboardPopupMenu,
103 xfdashboard_popup_menu,
104 XFDASHBOARD_TYPE_BACKGROUND,
105 G_ADD_PRIVATE(XfdashboardPopupMenu)
106 G_IMPLEMENT_INTERFACE(XFDASHBOARD_TYPE_FOCUSABLE, _xfdashboard_popup_menu_focusable_iface_init));
107
108 /* Properties */
109 enum
110 {
111 PROP_0,
112
113 PROP_DESTROY_ON_CANCEL,
114
115 PROP_SOURCE,
116
117 PROP_SHOW_TITLE,
118 PROP_TITLE,
119
120 PROP_SHOW_TITLE_ICON,
121 PROP_TITLE_ICON_NAME,
122 PROP_TITLE_GICON,
123
124 PROP_LAST
125 };
126
127 static GParamSpec* XfdashboardPopupMenuProperties[PROP_LAST]={ 0, };
128
129 /* Signals */
130 enum
131 {
132 SIGNAL_ACTIVATED,
133 SIGNAL_CANCELLED,
134
135 SIGNAL_ITEM_ACTIVATED,
136
137 SIGNAL_ITEM_ADDED,
138 SIGNAL_ITEM_REMOVED,
139
140 SIGNAL_LAST
141 };
142
143 static guint XfdashboardPopupMenuSignals[SIGNAL_LAST]={ 0, };
144
145
146 /* IMPLEMENTATION: Private variables and methods */
147
148 /* Suspension state of application changed */
_xfdashboard_popup_menu_on_application_suspended_changed(XfdashboardPopupMenu * self,GParamSpec * inSpec,gpointer inUserData)149 static void _xfdashboard_popup_menu_on_application_suspended_changed(XfdashboardPopupMenu *self,
150 GParamSpec *inSpec,
151 gpointer inUserData)
152 {
153 XfdashboardPopupMenuPrivate *priv;
154 XfdashboardApplication *application;
155 gboolean isSuspended;
156
157 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
158 g_return_if_fail(XFDASHBOARD_IS_APPLICATION(inUserData));
159
160 priv=self->priv;
161 application=XFDASHBOARD_APPLICATION(inUserData);
162
163 /* Get application suspend state */
164 isSuspended=xfdashboard_application_is_suspended(application);
165
166 /* If application is suspended then cancel pop-up menu */
167 if(isSuspended)
168 {
169 XFDASHBOARD_DEBUG(self, ACTOR,
170 "Cancel active pop-up menu '%s' for source %s@%p because application was suspended",
171 xfdashboard_popup_menu_get_title(self),
172 priv->source ? G_OBJECT_TYPE_NAME(priv->source) : "<nil>",
173 priv->source);
174
175 xfdashboard_popup_menu_cancel(self);
176 }
177 }
178
179 /* An event occured after a popup menu was activated so check if popup menu should
180 * be cancelled because a button was pressed and release outside the popup menu.
181 */
_xfdashboard_popup_menu_on_captured_event(XfdashboardPopupMenu * self,ClutterEvent * inEvent,gpointer inUserData)182 static gboolean _xfdashboard_popup_menu_on_captured_event(XfdashboardPopupMenu *self,
183 ClutterEvent *inEvent,
184 gpointer inUserData)
185 {
186 XfdashboardPopupMenuPrivate *priv;
187
188 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), CLUTTER_EVENT_PROPAGATE);
189 g_return_val_if_fail(XFDASHBOARD_IS_STAGE(inUserData), CLUTTER_EVENT_PROPAGATE);
190
191 priv=self->priv;
192
193 /* Check if popup menu should be cancelled depending on event */
194 switch(clutter_event_type(inEvent))
195 {
196 case CLUTTER_BUTTON_RELEASE:
197 /* If button was released outside popup menu cancel this popup menu */
198 {
199 gfloat x, y, w, h;
200
201 clutter_actor_get_transformed_position(CLUTTER_ACTOR(self), &x, &y);
202 clutter_actor_get_size(CLUTTER_ACTOR(self), &w, &h);
203 if(inEvent->button.x<x ||
204 inEvent->button.x>=(x+w) ||
205 inEvent->button.y<y ||
206 inEvent->button.y>=(y+h))
207 {
208 /* Cancel popup menu */
209 xfdashboard_popup_menu_cancel(self);
210
211 /* Do not let this event be handled */
212 return(CLUTTER_EVENT_STOP);
213 }
214 }
215 break;
216
217 case CLUTTER_KEY_PRESS:
218 case CLUTTER_KEY_RELEASE:
219 /* If key press or key release is not a selection action for a focusable
220 * actor then cancel this popup menu.
221 */
222 {
223 GSList *targetFocusables;
224 const gchar *action;
225 gboolean cancelPopupMenu;
226
227 /* Lookup action for event and emit action if a binding was found
228 * for this event.
229 */
230 targetFocusables=NULL;
231 action=NULL;
232 cancelPopupMenu=FALSE;
233
234 if(xfdashboard_focus_manager_get_event_targets_and_action(priv->focusManager, inEvent, XFDASHBOARD_FOCUSABLE(self), &targetFocusables, &action))
235 {
236 if(!targetFocusables ||
237 !targetFocusables->data ||
238 !XFDASHBOARD_IS_POPUP_MENU(targetFocusables->data))
239 {
240 cancelPopupMenu=TRUE;
241 }
242
243 /* Release allocated resources */
244 g_slist_free_full(targetFocusables, g_object_unref);
245 }
246
247 /* 'ESC' is a special key as it cannot be determined by focus
248 * manager but it has to be intercepted as this key release
249 * should only cancel popup-menu but not quit application.
250 */
251 if(!cancelPopupMenu &&
252 clutter_event_type(inEvent)==CLUTTER_KEY_RELEASE &&
253 inEvent->key.keyval==CLUTTER_KEY_Escape)
254 {
255 cancelPopupMenu=TRUE;
256 }
257
258 /* Cancel popup-menu if requested */
259 if(cancelPopupMenu)
260 {
261 /* Cancel popup menu */
262 xfdashboard_popup_menu_cancel(self);
263
264 /* Do not let this event be handled */
265 return(CLUTTER_EVENT_STOP);
266 }
267 }
268 break;
269
270 default:
271 /* Let all other event pass through */
272 break;
273 }
274
275 /* If we get here then this event passed our filter and can be handled normally */
276 return(CLUTTER_EVENT_PROPAGATE);
277 }
278
279 /* Check if menu item is really part of this pop-up menu */
_xfdashboard_popup_menu_contains_menu_item(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem)280 static gboolean _xfdashboard_popup_menu_contains_menu_item(XfdashboardPopupMenu *self,
281 XfdashboardPopupMenuItem *inMenuItem)
282 {
283 ClutterActor *parent;
284
285 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
286 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem), FALSE);
287
288 /* Iterate through parents and for each XfdashboardPopupMenu found, check
289 * if it is this pop-up menu and return TRUE if it is.
290 */
291 parent=clutter_actor_get_parent(CLUTTER_ACTOR(inMenuItem));
292 while(parent)
293 {
294 /* Check if current iterated parent is a XfdashboardPopupMenu and if it
295 * is this pop-up menu.
296 */
297 if(XFDASHBOARD_IS_POPUP_MENU(parent) &&
298 XFDASHBOARD_POPUP_MENU(parent)==self)
299 {
300 /* This one is this pop-up menu, so return TRUE here */
301 return(TRUE);
302 }
303
304 /* Continue with next parent */
305 parent=clutter_actor_get_parent(parent);
306 }
307
308 /* If we get here the "menu item" actor is a menu item of this pop-up menu */
309 return(FALSE);
310 }
311
312 /* Menu item was activated */
_xfdashboard_popup_menu_on_menu_item_activated(XfdashboardPopupMenu * self,gpointer inUserData)313 static void _xfdashboard_popup_menu_on_menu_item_activated(XfdashboardPopupMenu *self,
314 gpointer inUserData)
315 {
316 XfdashboardPopupMenuItem *menuItem;
317
318 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
319 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inUserData));
320
321 menuItem=XFDASHBOARD_POPUP_MENU_ITEM(inUserData);
322
323 /* Emit "item-activated" signal */
324 g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_ACTIVATED], 0, menuItem);
325
326 /* Cancel pop-up menu as menu item was activated and its callback function
327 * was called by its meta object.
328 */
329 xfdashboard_popup_menu_cancel(self);
330 }
331
332 /* Update visiblity of title actor depending on if title and/or icon of title
333 * should be shown or not.
334 */
_xfdashboard_popup_menu_update_title_actors_visibility(XfdashboardPopupMenu * self)335 static void _xfdashboard_popup_menu_update_title_actors_visibility(XfdashboardPopupMenu *self)
336 {
337 XfdashboardPopupMenuPrivate *priv;
338 XfdashboardLabelStyle oldStyle;
339 XfdashboardLabelStyle newStyle;
340 gboolean oldVisible;
341 gboolean newVisible;
342
343 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
344
345 priv=self->priv;
346
347 /* Get current visibility state */
348 oldVisible=clutter_actor_is_visible(priv->title);
349 oldStyle=xfdashboard_label_get_style(XFDASHBOARD_LABEL(priv->title));
350
351 /* Determine new visibility state depending on if title and/or icon of title
352 * should be shown or not.
353 */
354 newStyle=0;
355 newVisible=TRUE;
356 if(priv->showTitle && priv->showTitleIcon) newStyle=XFDASHBOARD_LABEL_STYLE_BOTH;
357 else if(priv->showTitle) newStyle=XFDASHBOARD_LABEL_STYLE_TEXT;
358 else if(priv->showTitleIcon) newStyle=XFDASHBOARD_LABEL_STYLE_ICON;
359 else newVisible=FALSE;
360
361 /* Set new visibility style if changed and re-layout title actor */
362 if(newStyle!=oldStyle)
363 {
364 xfdashboard_label_set_style(XFDASHBOARD_LABEL(priv->title), newStyle);
365 clutter_actor_queue_relayout(priv->title);
366 }
367
368 /* Show or hide actor */
369 if(newVisible!=oldVisible)
370 {
371 if(newVisible) clutter_actor_show(priv->title);
372 else clutter_actor_hide(priv->title);
373 }
374 }
375
376 /* The source actor was destroyed so cancel this pop-up menu if active and
377 * destroy it if automatic destruction was turned on.
378 */
_xfdashboard_popup_menu_on_source_destroy(XfdashboardPopupMenu * self,gpointer inUserData)379 static void _xfdashboard_popup_menu_on_source_destroy(XfdashboardPopupMenu *self,
380 gpointer inUserData)
381 {
382 XfdashboardPopupMenuPrivate *priv;
383
384 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
385 g_return_if_fail(CLUTTER_IS_ACTOR(inUserData));
386
387 priv=self->priv;
388
389 /* Unset and clean-up source */
390 if(priv->source)
391 {
392 gchar *cssClass;
393
394 /* Disconnect signal handler */
395 if(priv->sourceDestroySignalID)
396 {
397 g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
398 priv->sourceDestroySignalID=0;
399 }
400
401 /* Remove style */
402 cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
403 xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
404 g_free(cssClass);
405
406 /* Release source */
407 g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
408 priv->source=NULL;
409 }
410
411 /* Enforce that pop-up menu is cancelled either by calling the cancel function
412 * if it is active or by checking and destructing it if automatic destruction
413 * flag is set.
414 */
415 if(priv->isActive)
416 {
417 /* Pop-up menu is active so cancel it. The cancel function will also destroy
418 * it if destroy-on-cancel was enabled.
419 */
420 xfdashboard_popup_menu_cancel(self);
421 }
422 else
423 {
424 /* Destroy this pop-up menu actor when destroy-on-cancel was enabled */
425 if(priv->destroyOnCancel)
426 {
427 xfdashboard_actor_destroy(CLUTTER_ACTOR(self));
428 }
429 }
430 }
431
432
433 /* IMPLEMENTATION: ClutterActor */
434
435 /* Allocate position and size of actor and its children */
_xfdashboard_popup_menu_allocate(ClutterActor * inActor,const ClutterActorBox * inBox,ClutterAllocationFlags inFlags)436 static void _xfdashboard_popup_menu_allocate(ClutterActor *inActor,
437 const ClutterActorBox *inBox,
438 ClutterAllocationFlags inFlags)
439 {
440 ClutterAllocationFlags flags;
441
442 /* Chain up to store the allocation of the actor */
443 flags=inFlags | CLUTTER_DELEGATE_LAYOUT;
444 CLUTTER_ACTOR_CLASS(xfdashboard_popup_menu_parent_class)->allocate(inActor, inBox, flags);
445 }
446
447
448 /* IMPLEMENTATION: Interface XfdashboardFocusable */
449
450 /* Determine if actor can get the focus */
_xfdashboard_popup_menu_focusable_can_focus(XfdashboardFocusable * inFocusable)451 static gboolean _xfdashboard_popup_menu_focusable_can_focus(XfdashboardFocusable *inFocusable)
452 {
453 XfdashboardPopupMenu *self;
454 XfdashboardPopupMenuPrivate *priv;
455 XfdashboardFocusableInterface *selfIface;
456 XfdashboardFocusableInterface *parentIface;
457
458 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
459 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
460
461 self=XFDASHBOARD_POPUP_MENU(inFocusable);
462 priv=self->priv;
463
464 /* Call parent class interface function */
465 selfIface=XFDASHBOARD_FOCUSABLE_GET_IFACE(inFocusable);
466 parentIface=g_type_interface_peek_parent(selfIface);
467
468 if(parentIface && parentIface->can_focus)
469 {
470 if(!parentIface->can_focus(inFocusable))
471 {
472 return(FALSE);
473 }
474 }
475
476 /* Only active pop-up menus can be focused */
477 if(!priv->isActive) return(FALSE);
478
479 /* If we get here this actor can be focused */
480 return(TRUE);
481 }
482
483 /* Actor lost focus */
_xfdashboard_popup_menu_focusable_unset_focus(XfdashboardFocusable * inFocusable)484 static void _xfdashboard_popup_menu_focusable_unset_focus(XfdashboardFocusable *inFocusable)
485 {
486 XfdashboardPopupMenu *self;
487 XfdashboardPopupMenuPrivate *priv;
488 XfdashboardFocusableInterface *selfIface;
489 XfdashboardFocusableInterface *parentIface;
490
491 g_return_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable));
492 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable));
493
494 self=XFDASHBOARD_POPUP_MENU(inFocusable);
495 priv=self->priv;
496
497 /* Call parent class interface function */
498 selfIface=XFDASHBOARD_FOCUSABLE_GET_IFACE(inFocusable);
499 parentIface=g_type_interface_peek_parent(selfIface);
500
501 if(parentIface && parentIface->unset_focus)
502 {
503 parentIface->unset_focus(inFocusable);
504 }
505
506 /* If this pop-up menu is active (has flag set) then it was not cancelled and
507 * this actor lost its focus in any other way than expected. So do not refocus
508 * old remembered focusable as it may not be the one which has the focus before.
509 */
510 if(priv->isActive &&
511 priv->oldFocusable)
512 {
513 g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
514 priv->oldFocusable=NULL;
515 }
516
517 /* This actor lost focus so ensure that this popup menu is cancelled */
518 xfdashboard_popup_menu_cancel(self);
519 }
520
521 /* Determine if this actor supports selection */
_xfdashboard_popup_menu_focusable_supports_selection(XfdashboardFocusable * inFocusable)522 static gboolean _xfdashboard_popup_menu_focusable_supports_selection(XfdashboardFocusable *inFocusable)
523 {
524 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
525 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
526
527 /* This actor supports selection */
528 return(TRUE);
529 }
530
531 /* Get current selection */
_xfdashboard_popup_menu_focusable_get_selection(XfdashboardFocusable * inFocusable)532 static ClutterActor* _xfdashboard_popup_menu_focusable_get_selection(XfdashboardFocusable *inFocusable)
533 {
534 XfdashboardPopupMenu *self;
535 XfdashboardPopupMenuPrivate *priv;
536
537 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), NULL);
538 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), NULL);
539
540 self=XFDASHBOARD_POPUP_MENU(inFocusable);
541 priv=self->priv;
542
543 /* Return current selection */
544 return(priv->selectedItem);
545 }
546
547 /* Set new selection */
_xfdashboard_popup_menu_focusable_set_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection)548 static gboolean _xfdashboard_popup_menu_focusable_set_selection(XfdashboardFocusable *inFocusable,
549 ClutterActor *inSelection)
550 {
551 XfdashboardPopupMenu *self;
552 XfdashboardPopupMenuPrivate *priv;
553
554 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
555 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
556 g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), FALSE);
557
558 self=XFDASHBOARD_POPUP_MENU(inFocusable);
559 priv=self->priv;
560
561 /* Check that selection is a child of this actor */
562 if(inSelection &&
563 !clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
564 {
565 g_warning("%s is not a child of %s and cannot be selected",
566 G_OBJECT_TYPE_NAME(inSelection),
567 G_OBJECT_TYPE_NAME(self));
568
569 return(FALSE);
570 }
571
572 /* Remove weak reference at current selection */
573 if(priv->selectedItem)
574 {
575 g_object_remove_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
576 }
577
578 /* Set new selection */
579 priv->selectedItem=inSelection;
580
581 /* Add weak reference at new selection */
582 if(priv->selectedItem)
583 {
584 g_object_add_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
585 }
586
587 /* New selection was set successfully */
588 return(TRUE);
589 }
590
591 /* Find requested selection target depending of current selection */
_xfdashboard_popup_menu_focusable_find_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection,XfdashboardSelectionTarget inDirection)592 static ClutterActor* _xfdashboard_popup_menu_focusable_find_selection(XfdashboardFocusable *inFocusable,
593 ClutterActor *inSelection,
594 XfdashboardSelectionTarget inDirection)
595 {
596 XfdashboardPopupMenu *self;
597 XfdashboardPopupMenuPrivate *priv;
598 ClutterActor *selection;
599 ClutterActor *newSelection;
600 gchar *valueName;
601
602 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), NULL);
603 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), NULL);
604 g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), NULL);
605 g_return_val_if_fail(inDirection>=0 && inDirection<=XFDASHBOARD_SELECTION_TARGET_NEXT, NULL);
606
607 self=XFDASHBOARD_POPUP_MENU(inFocusable);
608 priv=self->priv;
609 selection=inSelection;
610 newSelection=NULL;
611
612 /* If there is nothing selected, select first actor and return */
613 if(!inSelection)
614 {
615 selection=clutter_actor_get_first_child(CLUTTER_ACTOR(priv->itemsContainer));
616
617 valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
618 XFDASHBOARD_DEBUG(self, ACTOR,
619 "No selection at %s, so select first child %s for direction %s",
620 G_OBJECT_TYPE_NAME(self),
621 selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
622 valueName);
623 g_free(valueName);
624
625 return(selection);
626 }
627
628 /* Check that selection is a child of this actor otherwise return NULL */
629 if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
630 {
631 ClutterActor *parent;
632
633 parent=clutter_actor_get_parent(inSelection);
634 g_warning("Cannot lookup selection target at %s because %s is a child of %s",
635 G_OBJECT_TYPE_NAME(self),
636 G_OBJECT_TYPE_NAME(inSelection),
637 parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>");
638
639 return(NULL);
640 }
641
642 /* Find target selection */
643 switch(inDirection)
644 {
645 case XFDASHBOARD_SELECTION_TARGET_UP:
646 newSelection=clutter_actor_get_previous_sibling(inSelection);
647 break;
648
649 case XFDASHBOARD_SELECTION_TARGET_DOWN:
650 newSelection=clutter_actor_get_next_sibling(inSelection);
651 break;
652
653 case XFDASHBOARD_SELECTION_TARGET_FIRST:
654 case XFDASHBOARD_SELECTION_TARGET_PAGE_UP:
655 newSelection=clutter_actor_get_first_child(CLUTTER_ACTOR(priv->itemsContainer));
656 break;
657
658 case XFDASHBOARD_SELECTION_TARGET_LAST:
659 case XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN:
660 newSelection=clutter_actor_get_last_child(CLUTTER_ACTOR(priv->itemsContainer));
661 break;
662
663 case XFDASHBOARD_SELECTION_TARGET_NEXT:
664 newSelection=clutter_actor_get_next_sibling(inSelection);
665 if(!newSelection) newSelection=clutter_actor_get_previous_sibling(inSelection);
666 break;
667
668 default:
669 {
670 valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
671 g_critical("Focusable object %s does not handle selection direction of type %s.",
672 G_OBJECT_TYPE_NAME(self),
673 valueName);
674 g_free(valueName);
675 }
676 break;
677 }
678
679 /* If new selection could be found override current selection with it */
680 if(newSelection) selection=newSelection;
681
682 /* Return new selection found */
683 XFDASHBOARD_DEBUG(self, ACTOR,
684 "Selecting %s at %s for current selection %s in direction %u",
685 selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
686 G_OBJECT_TYPE_NAME(self),
687 inSelection ? G_OBJECT_TYPE_NAME(inSelection) : "<nil>",
688 inDirection);
689
690 return(selection);
691 }
692
693 /* Activate selection */
_xfdashboard_popup_menu_focusable_activate_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection)694 static gboolean _xfdashboard_popup_menu_focusable_activate_selection(XfdashboardFocusable *inFocusable,
695 ClutterActor *inSelection)
696 {
697 XfdashboardPopupMenu *self;
698 XfdashboardPopupMenuItem *menuItem;
699
700 g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
701 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(inFocusable), FALSE);
702 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inSelection), FALSE);
703
704 self=XFDASHBOARD_POPUP_MENU(inFocusable);
705 menuItem=XFDASHBOARD_POPUP_MENU_ITEM(inSelection);
706
707 /* Check that selection is a child of this actor */
708 if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
709 {
710 ClutterActor *parent;
711
712 parent=clutter_actor_get_parent(inSelection);
713 g_warning("%s is a child of %s and cannot be activated at %s",
714 G_OBJECT_TYPE_NAME(inSelection),
715 parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>",
716 G_OBJECT_TYPE_NAME(self));
717
718 return(FALSE);
719 }
720
721 /* Activate selection */
722 xfdashboard_popup_menu_item_activate(menuItem);
723
724 /* If we get here activation of menu item was successful */
725 return(TRUE);
726 }
727
728 /* Interface initialization
729 * Set up default functions
730 */
_xfdashboard_popup_menu_focusable_iface_init(XfdashboardFocusableInterface * iface)731 void _xfdashboard_popup_menu_focusable_iface_init(XfdashboardFocusableInterface *iface)
732 {
733 iface->can_focus=_xfdashboard_popup_menu_focusable_can_focus;
734 iface->unset_focus=_xfdashboard_popup_menu_focusable_unset_focus;
735
736 iface->supports_selection=_xfdashboard_popup_menu_focusable_supports_selection;
737 iface->get_selection=_xfdashboard_popup_menu_focusable_get_selection;
738 iface->set_selection=_xfdashboard_popup_menu_focusable_set_selection;
739 iface->find_selection=_xfdashboard_popup_menu_focusable_find_selection;
740 iface->activate_selection=_xfdashboard_popup_menu_focusable_activate_selection;
741 }
742
743
744 /* IMPLEMENTATION: GObject */
745
746 /* Dispose this object */
_xfdashboard_popup_menu_dispose(GObject * inObject)747 static void _xfdashboard_popup_menu_dispose(GObject *inObject)
748 {
749 XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
750 XfdashboardPopupMenuPrivate *priv=self->priv;
751
752 /* Cancel this pop-up menu if it is still active */
753 xfdashboard_popup_menu_cancel(self);
754
755 /* Release our allocated variables */
756 if(priv->suspendSignalID)
757 {
758 g_signal_handler_disconnect(xfdashboard_application_get_default(), priv->suspendSignalID);
759 priv->suspendSignalID=0;
760 }
761
762 if(priv->capturedEventSignalID)
763 {
764 g_signal_handler_disconnect(priv->stage, priv->capturedEventSignalID);
765 priv->capturedEventSignalID=0;
766 }
767
768 if(priv->source)
769 {
770 gchar *cssClass;
771
772 /* Disconnect signal handler */
773 if(priv->sourceDestroySignalID)
774 {
775 g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
776 priv->sourceDestroySignalID=0;
777 }
778
779 /* Remove style */
780 cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
781 xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
782 g_free(cssClass);
783
784 /* Release source */
785 g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
786 priv->source=NULL;
787 }
788
789 if(priv->selectedItem)
790 {
791 g_object_remove_weak_pointer(G_OBJECT(priv->selectedItem), &priv->selectedItem);
792 priv->selectedItem=NULL;
793 }
794
795 if(priv->oldFocusable)
796 {
797 g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
798 priv->oldFocusable=NULL;
799 }
800
801 if(priv->itemsContainer)
802 {
803 clutter_actor_destroy(priv->itemsContainer);
804 priv->itemsContainer=NULL;
805 }
806
807 if(priv->focusManager)
808 {
809 xfdashboard_focus_manager_unregister(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
810 g_object_unref(priv->focusManager);
811 priv->focusManager=NULL;
812 }
813
814 if(priv->windowTracker)
815 {
816 g_object_unref(priv->windowTracker);
817 priv->windowTracker=NULL;
818 }
819
820 if(priv->stage)
821 {
822 priv->stage=NULL;
823 }
824
825 /* Call parent's class dispose method */
826 G_OBJECT_CLASS(xfdashboard_popup_menu_parent_class)->dispose(inObject);
827 }
828
829 /* Set/get properties */
_xfdashboard_popup_menu_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)830 static void _xfdashboard_popup_menu_set_property(GObject *inObject,
831 guint inPropID,
832 const GValue *inValue,
833 GParamSpec *inSpec)
834 {
835 XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
836
837 switch(inPropID)
838 {
839 case PROP_DESTROY_ON_CANCEL:
840 xfdashboard_popup_menu_set_destroy_on_cancel(self, g_value_get_boolean(inValue));
841 break;
842
843 case PROP_SOURCE:
844 xfdashboard_popup_menu_set_source(self, CLUTTER_ACTOR(g_value_get_object(inValue)));
845 break;
846
847 case PROP_SHOW_TITLE:
848 xfdashboard_popup_menu_set_show_title(self, g_value_get_boolean(inValue));
849 break;
850
851 case PROP_TITLE:
852 xfdashboard_popup_menu_set_title(self, g_value_get_string(inValue));
853 break;
854
855 case PROP_SHOW_TITLE_ICON:
856 xfdashboard_popup_menu_set_show_title_icon(self, g_value_get_boolean(inValue));
857 break;
858
859 case PROP_TITLE_ICON_NAME:
860 xfdashboard_popup_menu_set_title_icon_name(self, g_value_get_string(inValue));
861 break;
862
863 case PROP_TITLE_GICON:
864 xfdashboard_popup_menu_set_title_gicon(self, G_ICON(g_value_get_object(inValue)));
865 break;
866
867 default:
868 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
869 break;
870 }
871 }
872
_xfdashboard_popup_menu_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)873 static void _xfdashboard_popup_menu_get_property(GObject *inObject,
874 guint inPropID,
875 GValue *outValue,
876 GParamSpec *inSpec)
877 {
878 XfdashboardPopupMenu *self=XFDASHBOARD_POPUP_MENU(inObject);
879 XfdashboardPopupMenuPrivate *priv=self->priv;
880
881 switch(inPropID)
882 {
883 case PROP_DESTROY_ON_CANCEL:
884 g_value_set_boolean(outValue, priv->destroyOnCancel);
885 break;
886
887 case PROP_SOURCE:
888 g_value_set_object(outValue, priv->source);
889 break;
890
891 case PROP_SHOW_TITLE:
892 g_value_set_boolean(outValue, priv->showTitle);
893 break;
894
895 case PROP_TITLE:
896 g_value_set_string(outValue, xfdashboard_popup_menu_get_title(self));
897 break;
898
899 case PROP_SHOW_TITLE_ICON:
900 g_value_set_boolean(outValue, priv->showTitleIcon);
901 break;
902
903 case PROP_TITLE_ICON_NAME:
904 g_value_set_string(outValue, xfdashboard_popup_menu_get_title_icon_name(self));
905 break;
906
907 case PROP_TITLE_GICON:
908 g_value_set_object(outValue, xfdashboard_popup_menu_get_title_gicon(self));
909 break;
910
911 default:
912 G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
913 break;
914 }
915 }
916
917 /* Class initialization
918 * Override functions in parent classes and define properties
919 * and signals
920 */
xfdashboard_popup_menu_class_init(XfdashboardPopupMenuClass * klass)921 static void xfdashboard_popup_menu_class_init(XfdashboardPopupMenuClass *klass)
922 {
923 XfdashboardActorClass *actorClass=XFDASHBOARD_ACTOR_CLASS(klass);
924 ClutterActorClass *clutterActorClass=CLUTTER_ACTOR_CLASS(klass);
925 GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
926
927 /* Override functions */
928 gobjectClass->dispose=_xfdashboard_popup_menu_dispose;
929 gobjectClass->set_property=_xfdashboard_popup_menu_set_property;
930 gobjectClass->get_property=_xfdashboard_popup_menu_get_property;
931
932 clutterActorClass->allocate=_xfdashboard_popup_menu_allocate;
933
934 /* Define properties */
935 /**
936 * XfdashboardPopupMenu:destroy-on-cancel:
937 *
938 * A flag indicating if this pop-up menu should be destroyed automatically
939 * when it is cancelled.
940 */
941 XfdashboardPopupMenuProperties[PROP_DESTROY_ON_CANCEL]=
942 g_param_spec_boolean("destroy-on-cancel",
943 "Destroy on cancel",
944 "Flag indicating this pop-up menu should be destroyed automatically when it is cancelled",
945 FALSE,
946 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
947
948 /**
949 * XfdashboardPopupMenu:source:
950 *
951 * The #ClutterActor on which this pop-up menu depends on. If this actor is
952 * destroyed then this pop-up menu is cancelled when active.
953 */
954 XfdashboardPopupMenuProperties[PROP_SOURCE]=
955 g_param_spec_object("source",
956 "Source",
957 "The object on which this pop-up menu depends on",
958 CLUTTER_TYPE_ACTOR,
959 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
960
961 /**
962 * XfdashboardPopupMenu:show-title:
963 *
964 * A flag indicating if the title of this pop-up menu should be shown.
965 */
966 XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]=
967 g_param_spec_boolean("show-title",
968 "Show title",
969 "Flag indicating if the title of this pop-up menu should be shown",
970 FALSE,
971 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
972
973 /**
974 * XfdashboardPopupMenu:title:
975 *
976 * A string containing the title of this pop-up menu.
977 */
978 XfdashboardPopupMenuProperties[PROP_TITLE]=
979 g_param_spec_string("title",
980 "Title",
981 "Title of pop-up menu",
982 N_(""),
983 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
984
985 /**
986 * XfdashboardPopupMenu:show-title-icon:
987 *
988 * A flag indicating if the icon of the title of this pop-up menu should be shown.
989 */
990 XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]=
991 g_param_spec_boolean("show-title-icon",
992 "Show title icon",
993 "Flag indicating if the icon of title of this pop-up menu should be shown",
994 FALSE,
995 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
996
997 /**
998 * XfdashboardPopupMenu:title-icon-name:
999 *
1000 * A string containing the stock icon name or file name for the icon to use
1001 * at title of this pop-up menu.
1002 */
1003 XfdashboardPopupMenuProperties[PROP_TITLE_ICON_NAME]=
1004 g_param_spec_string("title-icon-name",
1005 "Title icon name",
1006 "Themed icon name or file name of icon used in title",
1007 N_(""),
1008 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1009
1010 /**
1011 * XfdashboardPopupMenu:title-gicon:
1012 *
1013 * A #GIcon containing the icon image to use at title of this pop-up menu.
1014 */
1015 XfdashboardPopupMenuProperties[PROP_TITLE_GICON]=
1016 g_param_spec_object("title-gicon",
1017 "Title GIcon",
1018 "The GIcon of icon used in title",
1019 G_TYPE_ICON,
1020 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
1021
1022 g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardPopupMenuProperties);
1023
1024 /* Define stylable properties */
1025 xfdashboard_actor_install_stylable_property(actorClass, XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]);
1026 xfdashboard_actor_install_stylable_property(actorClass, XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]);
1027
1028 /* Define signals */
1029 /**
1030 * XfdashboardPopupMenu::activated:
1031 * @self: The pop-up menu which was activated
1032 *
1033 * The ::activated signal is emitted when the pop-up menu is shown and the
1034 * user can perform an action by selecting an item.
1035 */
1036 XfdashboardPopupMenuSignals[SIGNAL_ACTIVATED]=
1037 g_signal_new("activated",
1038 G_TYPE_FROM_CLASS(klass),
1039 G_SIGNAL_RUN_LAST,
1040 G_STRUCT_OFFSET(XfdashboardPopupMenuClass, activated),
1041 NULL,
1042 NULL,
1043 g_cclosure_marshal_VOID__VOID,
1044 G_TYPE_NONE,
1045 0);
1046
1047 /**
1048 * XfdashboardPopupMenu::cancelled:
1049 * @self: The pop-up menu which was cancelled
1050 *
1051 * The ::cancelled signal is emitted when the pop-up menu is hidden. This
1052 * signal is emitted regardless the user has chosen an item and perform the
1053 * associated action or not.
1054 *
1055 * Note: This signal does not indicate if a selection was made or not.
1056 */
1057 XfdashboardPopupMenuSignals[SIGNAL_CANCELLED]=
1058 g_signal_new("cancelled",
1059 G_TYPE_FROM_CLASS(klass),
1060 G_SIGNAL_RUN_LAST,
1061 G_STRUCT_OFFSET(XfdashboardPopupMenuClass, cancelled),
1062 NULL,
1063 NULL,
1064 g_cclosure_marshal_VOID__VOID,
1065 G_TYPE_NONE,
1066 0);
1067
1068 /**
1069 * XfdashboardPopupMenu::item-activated:
1070 * @self: The pop-up menu containing the activated menu item
1071 * @inMenuItem: The menu item which was activated
1072 *
1073 * The ::item-activated signal is emitted when a menu item at pop-up menu
1074 * was activated either by key-press or by clicking on it.
1075 */
1076 XfdashboardPopupMenuSignals[SIGNAL_ITEM_ACTIVATED]=
1077 g_signal_new("item-activated",
1078 G_TYPE_FROM_CLASS(klass),
1079 G_SIGNAL_RUN_LAST,
1080 G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_activated),
1081 NULL,
1082 NULL,
1083 g_cclosure_marshal_VOID__OBJECT,
1084 G_TYPE_NONE,
1085 1,
1086 XFDASHBOARD_TYPE_POPUP_MENU_ITEM);
1087
1088 /**
1089 * XfdashboardPopupMenu::item-added:
1090 * @self: The pop-up menu containing the activated menu item
1091 * @inMenuItem: The menu item which was added
1092 *
1093 * The ::item-added signal is emitted when a menu item was added to pop-up menu.
1094 */
1095 XfdashboardPopupMenuSignals[SIGNAL_ITEM_ADDED]=
1096 g_signal_new("item-added",
1097 G_TYPE_FROM_CLASS(klass),
1098 G_SIGNAL_RUN_LAST,
1099 G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_added),
1100 NULL,
1101 NULL,
1102 g_cclosure_marshal_VOID__OBJECT,
1103 G_TYPE_NONE,
1104 1,
1105 XFDASHBOARD_TYPE_POPUP_MENU_ITEM);
1106
1107 /**
1108 * XfdashboardPopupMenu::item-removed:
1109 * @self: The pop-up menu containing the activated menu item
1110 * @inMenuItem: The menu item which was added
1111 *
1112 * The ::item-added signal is emitted when a menu item was added to pop-up menu.
1113 */
1114 XfdashboardPopupMenuSignals[SIGNAL_ITEM_REMOVED]=
1115 g_signal_new("item-removed",
1116 G_TYPE_FROM_CLASS(klass),
1117 G_SIGNAL_RUN_LAST,
1118 G_STRUCT_OFFSET(XfdashboardPopupMenuClass, item_removed),
1119 NULL,
1120 NULL,
1121 g_cclosure_marshal_VOID__OBJECT,
1122 G_TYPE_NONE,
1123 1,
1124 XFDASHBOARD_TYPE_POPUP_MENU_ITEM);
1125 }
1126
1127 /* Object initialization
1128 * Create private structure and set up default values
1129 */
xfdashboard_popup_menu_init(XfdashboardPopupMenu * self)1130 static void xfdashboard_popup_menu_init(XfdashboardPopupMenu *self)
1131 {
1132 XfdashboardPopupMenuPrivate *priv;
1133 ClutterLayoutManager *layout;
1134
1135 priv=self->priv=xfdashboard_popup_menu_get_instance_private(self);
1136
1137 /* Set up default values */
1138 priv->destroyOnCancel=FALSE;
1139 priv->source=NULL;
1140 priv->showTitle=FALSE;
1141 priv->showTitleIcon=FALSE;
1142 priv->isActive=FALSE;
1143 priv->title=NULL;
1144 priv->itemsContainer=NULL;
1145 priv->windowTracker=xfdashboard_window_tracker_get_default();
1146 priv->focusManager=xfdashboard_focus_manager_get_default();
1147 priv->oldFocusable=NULL;
1148 priv->selectedItem=NULL;
1149 priv->stage=NULL;
1150 priv->capturedEventSignalID=0;
1151 priv->sourceDestroySignalID=0;
1152 priv->suspendSignalID=0;
1153
1154 /* This actor is react on events */
1155 clutter_actor_set_reactive(CLUTTER_ACTOR(self), TRUE);
1156
1157 /* Set up title actor */
1158 priv->title=xfdashboard_button_new();
1159 xfdashboard_label_set_style(XFDASHBOARD_LABEL(priv->title), XFDASHBOARD_LABEL_STYLE_TEXT);
1160 xfdashboard_label_set_text(XFDASHBOARD_LABEL(priv->title), NULL);
1161 clutter_actor_set_x_expand(priv->title, TRUE);
1162 clutter_actor_set_y_expand(priv->title, TRUE);
1163 clutter_actor_hide(priv->title);
1164 xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->title), "popup-menu-title");
1165
1166 /* Set up items container which will hold all menu items */
1167 layout=xfdashboard_box_layout_new();
1168 clutter_box_layout_set_orientation(CLUTTER_BOX_LAYOUT(layout), CLUTTER_ORIENTATION_VERTICAL);
1169
1170 priv->itemsContainer=xfdashboard_actor_new();
1171 clutter_actor_set_x_expand(priv->itemsContainer, TRUE);
1172 clutter_actor_set_y_expand(priv->itemsContainer, TRUE);
1173 clutter_actor_set_layout_manager(priv->itemsContainer, layout);
1174
1175 /* Set up this actor */
1176 layout=xfdashboard_box_layout_new();
1177 clutter_box_layout_set_orientation(CLUTTER_BOX_LAYOUT(layout), CLUTTER_ORIENTATION_VERTICAL);
1178 clutter_actor_set_layout_manager(CLUTTER_ACTOR(self), layout);
1179
1180 clutter_actor_add_child(CLUTTER_ACTOR(self), priv->title);
1181 clutter_actor_add_child(CLUTTER_ACTOR(self), priv->itemsContainer);
1182 xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(self), "popup-menu");
1183
1184 /* Register this actor at focus manager but ensure that this actor is
1185 * not focusable initially */
1186 xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), FALSE);
1187 xfdashboard_focus_manager_register(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
1188
1189 /* Add popup menu to stage */
1190 priv->stage=xfdashboard_application_get_stage(xfdashboard_application_get_default());
1191 clutter_actor_insert_child_above(CLUTTER_ACTOR(priv->stage), CLUTTER_ACTOR(self), NULL);
1192
1193 /* Connect signal to get notified when application suspends to cancel pop-up menu */
1194 priv->suspendSignalID=g_signal_connect_swapped(xfdashboard_application_get_default(),
1195 "notify::is-suspended",
1196 G_CALLBACK(_xfdashboard_popup_menu_on_application_suspended_changed),
1197 self);
1198 }
1199
1200 /* IMPLEMENTATION: Public API */
1201
1202 /**
1203 * xfdashboard_popup_menu_new:
1204 *
1205 * Creates a new #XfdashboardPopupMenu actor
1206 *
1207 * Return value: The newly created #XfdashboardPopupMenu
1208 */
xfdashboard_popup_menu_new(void)1209 ClutterActor* xfdashboard_popup_menu_new(void)
1210 {
1211 return(g_object_new(XFDASHBOARD_TYPE_POPUP_MENU, NULL));
1212 }
1213
1214 /**
1215 * xfdashboard_popup_menu_new_for_source:
1216 * @inSource: A #ClutterActor which this pop-up menu should depend on
1217 *
1218 * Creates a new #XfdashboardPopupMenu actor which depends on actor @inSource.
1219 * When the actor @inSource is destroyed and the pop-up menu is active then it
1220 * will be cancelled automatically.
1221 *
1222 * Return value: The newly created #XfdashboardPopupMenu
1223 */
xfdashboard_popup_menu_new_for_source(ClutterActor * inSource)1224 ClutterActor* xfdashboard_popup_menu_new_for_source(ClutterActor *inSource)
1225 {
1226 g_return_val_if_fail(CLUTTER_IS_ACTOR(inSource), NULL);
1227
1228 return(g_object_new(XFDASHBOARD_TYPE_POPUP_MENU,
1229 "source", inSource,
1230 NULL));
1231 }
1232
1233 /**
1234 * xfdashboard_popup_menu_get_destroy_on_cancel:
1235 * @self: A #XfdashboardPopupMenu
1236 *
1237 * Retrieves the automatic destruction mode of @self. If automatic destruction mode
1238 * is %TRUE then the pop-up menu will be destroy by calling xfdashboard_actor_destroy()
1239 * when it is cancelled, e.g. by calling xfdashboard_popup_menu_cancel().
1240 *
1241 * Return value: Returns %TRUE if automatic destruction mode is enabled, otherwise
1242 * %FALSE.
1243 */
xfdashboard_popup_menu_get_destroy_on_cancel(XfdashboardPopupMenu * self)1244 gboolean xfdashboard_popup_menu_get_destroy_on_cancel(XfdashboardPopupMenu *self)
1245 {
1246 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
1247
1248 return(self->priv->destroyOnCancel);
1249 }
1250
1251 /**
1252 * xfdashboard_popup_menu_set_destroy_on_cancel:
1253 * @self: A #XfdashboardPopupMenu
1254 * @inDestroyOnCancel: The automatic destruction mode to set at @self
1255 *
1256 * Sets the automatic destruction mode of @self. If @inDestroyOnCancel is set to
1257 * %TRUE then the pop-up menu will automatically destroyed by calling xfdashboard_actor_destroy()
1258 * when it is cancelled, e.g. by calling xfdashboard_popup_menu_cancel().
1259 */
xfdashboard_popup_menu_set_destroy_on_cancel(XfdashboardPopupMenu * self,gboolean inDestroyOnCancel)1260 void xfdashboard_popup_menu_set_destroy_on_cancel(XfdashboardPopupMenu *self, gboolean inDestroyOnCancel)
1261 {
1262 XfdashboardPopupMenuPrivate *priv;
1263
1264 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1265
1266 priv=self->priv;
1267
1268 /* Set value if changed */
1269 if(priv->destroyOnCancel!=inDestroyOnCancel)
1270 {
1271 /* Set value */
1272 priv->destroyOnCancel=inDestroyOnCancel;
1273
1274 /* Notify about property change */
1275 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_DESTROY_ON_CANCEL]);
1276 }
1277 }
1278
1279 /**
1280 * xfdashboard_popup_menu_get_source:
1281 * @self: A #XfdashboardPopupMenu
1282 *
1283 * Retrieves the source actor to @inSource which the pop-up menu at @self depends on.
1284 *
1285 * Return value: (transfer none): Returns #ClutterActor which the pop-up menu or
1286 * %NULL if no source actor is set.
1287 */
xfdashboard_popup_menu_get_source(XfdashboardPopupMenu * self)1288 ClutterActor* xfdashboard_popup_menu_get_source(XfdashboardPopupMenu *self)
1289 {
1290 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
1291
1292 return(self->priv->source);
1293 }
1294
1295 /**
1296 * xfdashboard_popup_menu_set_source:
1297 * @self: A #XfdashboardPopupMenu
1298 * @inSource: A #ClutterActor which this pop-up menu should depend on or %NULL
1299 * if it should not depend on any actor
1300 *
1301 * Sets the source actor to @inSource which the pop-up menu at @self depends on.
1302 * When the actor @inSource is destroyed and the pop-up menu at @self is active
1303 * then it will be cancelled automatically.
1304 *
1305 * In addition the CSS class "popup-menu-source-SOURCE_CLASS_NAME" will be set
1306 * on pop-up menu at @self, e.g. if source is of type ClutterActor the CSS class
1307 * "popup-menu-source-ClutterActor" will be set.
1308 */
xfdashboard_popup_menu_set_source(XfdashboardPopupMenu * self,ClutterActor * inSource)1309 void xfdashboard_popup_menu_set_source(XfdashboardPopupMenu *self, ClutterActor *inSource)
1310 {
1311 XfdashboardPopupMenuPrivate *priv;
1312 gchar *cssClass;
1313
1314 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1315 g_return_if_fail(!inSource || CLUTTER_IS_ACTOR(inSource));
1316
1317 priv=self->priv;
1318
1319 /* Set value if changed */
1320 if(priv->source!=inSource)
1321 {
1322 /* Release old source if set */
1323 if(priv->source)
1324 {
1325 /* Disconnect signal handler */
1326 g_signal_handler_disconnect(priv->source, priv->sourceDestroySignalID);
1327 priv->sourceDestroySignalID=0;
1328
1329 /* Remove style */
1330 cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
1331 xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(self), cssClass);
1332 g_free(cssClass);
1333
1334 /* Release source */
1335 g_object_remove_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
1336 priv->source=NULL;
1337 }
1338
1339 /* Set value */
1340 if(inSource)
1341 {
1342 /* Set up source */
1343 priv->source=inSource;
1344 g_object_add_weak_pointer(G_OBJECT(priv->source), (gpointer*)&priv->source);
1345
1346 /* Add style */
1347 cssClass=g_strdup_printf("popup-menu-source-%s", G_OBJECT_TYPE_NAME(priv->source));
1348 xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(self), cssClass);
1349 g_free(cssClass);
1350
1351 /* Connect signal handler */
1352 priv->sourceDestroySignalID=g_signal_connect_swapped(priv->source,
1353 "destroy",
1354 G_CALLBACK(_xfdashboard_popup_menu_on_source_destroy),
1355 self);
1356 }
1357
1358 /* Notify about property change */
1359 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SOURCE]);
1360 }
1361 }
1362
1363 /**
1364 * xfdashboard_popup_menu_get_show_title:
1365 * @self: A #XfdashboardPopupMenu
1366 *
1367 * Retrieves the state if the title of pop-up menu at @self should be shown or not.
1368 *
1369 * Return value: Returns %TRUE if title of pop-up menu should be shown and
1370 * %FALSE if not.
1371 */
xfdashboard_popup_menu_get_show_title(XfdashboardPopupMenu * self)1372 gboolean xfdashboard_popup_menu_get_show_title(XfdashboardPopupMenu *self)
1373 {
1374 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
1375
1376 return(self->priv->showTitle);
1377 }
1378
1379 /**
1380 * xfdashboard_popup_menu_set_show_title:
1381 * @self: A #XfdashboardPopupMenu
1382 * @inShowTitle: Flag indicating if the title of pop-up menu should be shown.
1383 *
1384 * If @inShowTitle is %TRUE then the title of the pop-up menu at @self will be
1385 * shown. If @inShowTitle is %FALSE it will be hidden.
1386 */
xfdashboard_popup_menu_set_show_title(XfdashboardPopupMenu * self,gboolean inShowTitle)1387 void xfdashboard_popup_menu_set_show_title(XfdashboardPopupMenu *self, gboolean inShowTitle)
1388 {
1389 XfdashboardPopupMenuPrivate *priv;
1390
1391 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1392
1393 priv=self->priv;
1394
1395 /* Set value if changed */
1396 if(priv->showTitle!=inShowTitle)
1397 {
1398 /* Set value */
1399 priv->showTitle=inShowTitle;
1400
1401 /* Update visibility state of title actor */
1402 _xfdashboard_popup_menu_update_title_actors_visibility(self);
1403
1404 /* Notify about property change */
1405 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SHOW_TITLE]);
1406 }
1407 }
1408
1409 /**
1410 * xfdashboard_popup_menu_get_title:
1411 * @self: A #XfdashboardPopupMenu
1412 *
1413 * Retrieves the title of pop-up menu.
1414 *
1415 * Return value: Returns string with title of pop-up menu.
1416 */
xfdashboard_popup_menu_get_title(XfdashboardPopupMenu * self)1417 const gchar* xfdashboard_popup_menu_get_title(XfdashboardPopupMenu *self)
1418 {
1419 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
1420
1421 return(xfdashboard_label_get_text(XFDASHBOARD_LABEL(self->priv->title)));
1422 }
1423
1424 /**
1425 * xfdashboard_popup_menu_set_title:
1426 * @self: A #XfdashboardPopupMenu
1427 * @inMarkupTitle: The title to set
1428 *
1429 * Sets @inMarkupTitle as title of pop-up menu at @self. The title string can
1430 * contain markup.
1431 */
xfdashboard_popup_menu_set_title(XfdashboardPopupMenu * self,const gchar * inMarkupTitle)1432 void xfdashboard_popup_menu_set_title(XfdashboardPopupMenu *self, const gchar *inMarkupTitle)
1433 {
1434 XfdashboardPopupMenuPrivate *priv;
1435
1436 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1437 g_return_if_fail(inMarkupTitle);
1438
1439 priv=self->priv;
1440
1441 /* Set value if changed */
1442 if(g_strcmp0(xfdashboard_label_get_text(XFDASHBOARD_LABEL(priv->title)), inMarkupTitle)!=0)
1443 {
1444 /* Set value */
1445 xfdashboard_label_set_text(XFDASHBOARD_LABEL(priv->title), inMarkupTitle);
1446
1447 /* Notify about property change */
1448 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE]);
1449 }
1450 }
1451
1452 /**
1453 * xfdashboard_popup_menu_get_show_title_icon:
1454 * @self: A #XfdashboardPopupMenu
1455 *
1456 * Retrieves the state if the icon of title of pop-up menu at @self should be
1457 * shown or not.
1458 *
1459 * Return value: Returns %TRUE if icon of title of pop-up menu should be shown
1460 * and %FALSE if not.
1461 */
xfdashboard_popup_menu_get_show_title_icon(XfdashboardPopupMenu * self)1462 gboolean xfdashboard_popup_menu_get_show_title_icon(XfdashboardPopupMenu *self)
1463 {
1464 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
1465
1466 return(self->priv->showTitleIcon);
1467 }
1468
1469 /**
1470 * xfdashboard_popup_menu_set_show_title_icon:
1471 * @self: A #XfdashboardPopupMenu
1472 * @inShowTitle: Flag indicating if the icon of title of pop-up menu should be shown.
1473 *
1474 * If @inShowTitle is %TRUE then the icon of title of the pop-up menu at @self will be
1475 * shown. If @inShowTitle is %FALSE it will be hidden.
1476 */
xfdashboard_popup_menu_set_show_title_icon(XfdashboardPopupMenu * self,gboolean inShowTitleIcon)1477 void xfdashboard_popup_menu_set_show_title_icon(XfdashboardPopupMenu *self, gboolean inShowTitleIcon)
1478 {
1479 XfdashboardPopupMenuPrivate *priv;
1480
1481 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1482
1483 priv=self->priv;
1484
1485 /* Set value if changed */
1486 if(priv->showTitleIcon!=inShowTitleIcon)
1487 {
1488 /* Set value */
1489 priv->showTitleIcon=inShowTitleIcon;
1490
1491 /* Update visibility state of title actor */
1492 _xfdashboard_popup_menu_update_title_actors_visibility(self);
1493
1494 /* Notify about property change */
1495 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_SHOW_TITLE_ICON]);
1496 }
1497 }
1498
1499 /**
1500 * xfdashboard_popup_menu_get_title_icon_name:
1501 * @self: A #XfdashboardPopupMenu
1502 *
1503 * Retrieves the stock icon name or file name of title icon of pop-up menu at @self.
1504 *
1505 * Return value: Returns string with icon name or file name of pop-up menu's title.
1506 */
xfdashboard_popup_menu_get_title_icon_name(XfdashboardPopupMenu * self)1507 const gchar* xfdashboard_popup_menu_get_title_icon_name(XfdashboardPopupMenu *self)
1508 {
1509 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
1510
1511 return(xfdashboard_label_get_icon_name(XFDASHBOARD_LABEL(self->priv->title)));
1512 }
1513
1514 /**
1515 * xfdashboard_popup_menu_set_title_icon_name:
1516 * @self: A #XfdashboardPopupMenu
1517 * @inIconName: A string containing the stock icon name or file name for the icon
1518 * to be place in the toogle button
1519 *
1520 * Sets the icon in title to icon at @inIconName of pop-up menu at @self. If set to
1521 * %NULL the title icon is hidden.
1522 */
xfdashboard_popup_menu_set_title_icon_name(XfdashboardPopupMenu * self,const gchar * inIconName)1523 void xfdashboard_popup_menu_set_title_icon_name(XfdashboardPopupMenu *self, const gchar *inIconName)
1524 {
1525 XfdashboardPopupMenuPrivate *priv;
1526
1527 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1528 g_return_if_fail(inIconName);
1529
1530 priv=self->priv;
1531
1532 /* Set value if changed */
1533 if(g_strcmp0(xfdashboard_label_get_icon_name(XFDASHBOARD_LABEL(priv->title)), inIconName)!=0)
1534 {
1535 /* Set value */
1536 xfdashboard_label_set_icon_name(XFDASHBOARD_LABEL(priv->title), inIconName);
1537
1538 /* Notify about property change */
1539 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE_ICON_NAME]);
1540 }
1541 }
1542
1543 /**
1544 * xfdashboard_popup_menu_get_title_gicon:
1545 * @self: A #XfdashboardPopupMenu
1546 *
1547 * Retrieves the title's icon of type #GIcon of pop-up menu at @self.
1548 *
1549 * Return value: Returns #GIcon of pop-up menu's title.
1550 */
xfdashboard_popup_menu_get_title_gicon(XfdashboardPopupMenu * self)1551 GIcon* xfdashboard_popup_menu_get_title_gicon(XfdashboardPopupMenu *self)
1552 {
1553 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
1554
1555 return(xfdashboard_label_get_gicon(XFDASHBOARD_LABEL(self->priv->title)));
1556 }
1557
1558 /**
1559 * xfdashboard_popup_menu_set_title_gicon:
1560 * @self: A #XfdashboardPopupMenu
1561 * @inIcon: A #GIcon containing the icon image
1562 *
1563 * Sets the icon in title to icon at @inIcon of pop-up menu at @self. If set to
1564 * %NULL the title icon is hidden.
1565 */
xfdashboard_popup_menu_set_title_gicon(XfdashboardPopupMenu * self,GIcon * inIcon)1566 void xfdashboard_popup_menu_set_title_gicon(XfdashboardPopupMenu *self, GIcon *inIcon)
1567 {
1568 XfdashboardPopupMenuPrivate *priv;
1569 GIcon *icon;
1570
1571 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1572 g_return_if_fail(G_IS_ICON(inIcon));
1573
1574 priv=self->priv;
1575
1576 /* Set value if changed */
1577 icon=xfdashboard_label_get_gicon(XFDASHBOARD_LABEL(priv->title));
1578 if(icon!=inIcon ||
1579 (icon && inIcon && !g_icon_equal(icon, inIcon)))
1580 {
1581 /* Set value */
1582 xfdashboard_label_set_gicon(XFDASHBOARD_LABEL(priv->title), inIcon);
1583
1584 /* Notify about property change */
1585 g_object_notify_by_pspec(G_OBJECT(self), XfdashboardPopupMenuProperties[PROP_TITLE_GICON]);
1586 }
1587 }
1588
1589 /**
1590 * xfdashboard_popup_menu_add_item:
1591 * @self: A #XfdashboardPopupMenu
1592 * @inMenuItem: A #XfdashboardPopupMenuItem to add to pop-up menu
1593 *
1594 * Adds the actor @inMenuItem to end of pop-up menu.
1595 *
1596 * If menu item actor implements the #XfdashboardStylable interface the CSS class
1597 * popup-menu-item will be added.
1598 *
1599 * Return value: Returns index where item was inserted at or -1 if it failed.
1600 */
xfdashboard_popup_menu_add_item(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem)1601 gint xfdashboard_popup_menu_add_item(XfdashboardPopupMenu *self,
1602 XfdashboardPopupMenuItem *inMenuItem)
1603 {
1604 return(xfdashboard_popup_menu_insert_item(self, inMenuItem, -1));
1605 }
1606
1607 /**
1608 * xfdashboard_popup_menu_insert_item:
1609 * @self: A #XfdashboardPopupMenu
1610 * @inMenuItem: A #XfdashboardPopupMenuItem to add to pop-up menu
1611 * @inIndex: The position where to insert this item at
1612 *
1613 * Inserts the actor @inMenuItem at position @inIndex into pop-up menu.
1614 *
1615 * If position @inIndex is greater than the number of menu items in @self or is
1616 * less than 0, then the menu item actor @inMenuItem is added to end to
1617 * pop-up menu.
1618 *
1619 * If menu item actor implements the #XfdashboardStylable interface the CSS class
1620 * popup-menu-item will be added.
1621 *
1622 * Return value: Returns index where item was inserted at or -1 if it failed.
1623 */
xfdashboard_popup_menu_insert_item(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem,gint inIndex)1624 gint xfdashboard_popup_menu_insert_item(XfdashboardPopupMenu *self,
1625 XfdashboardPopupMenuItem *inMenuItem,
1626 gint inIndex)
1627 {
1628 XfdashboardPopupMenuPrivate *priv;
1629
1630 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), -1);
1631 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem), -1);
1632 g_return_val_if_fail(clutter_actor_get_parent(CLUTTER_ACTOR(inMenuItem))==NULL, -1);
1633
1634 priv=self->priv;
1635
1636 /* Insert menu item actor to container at requested position */
1637 clutter_actor_insert_child_at_index(priv->itemsContainer, CLUTTER_ACTOR(inMenuItem), inIndex);
1638
1639 /* Add CSS class 'popup-menu-item' to newly added menu item */
1640 if(XFDASHBOARD_IS_STYLABLE(inMenuItem))
1641 {
1642 xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(inMenuItem), "popup-menu-item");
1643 }
1644
1645 /* Connect signal to get notified when user made a selection to cancel pop-up
1646 * menu but ensure that it is called nearly at last because the pop-up menu
1647 * could be configured to get destroyed automatically when user selected an
1648 * item (or cancelled the menu). In this case other signal handler may not be
1649 * called if pop-up menu's signal handler is called before. By calling it at
1650 * last all other normally connected signal handlers will get be called.
1651 */
1652 g_signal_connect_data(inMenuItem,
1653 "activated",
1654 G_CALLBACK(_xfdashboard_popup_menu_on_menu_item_activated),
1655 self,
1656 NULL,
1657 G_CONNECT_AFTER | G_CONNECT_SWAPPED);
1658
1659 /* Emit signal */
1660 g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_ADDED], 0, inMenuItem);
1661
1662 /* Get index where menu item actor was inserted at */
1663 return(xfdashboard_popup_menu_get_item_index(self, inMenuItem));
1664 }
1665
1666 /**
1667 * xfdashboard_popup_menu_move_item:
1668 * @self: A #XfdashboardPopupMenu
1669 * @inMenuItem: A #XfdashboardPopupMenuItem menu item to move
1670 * @inIndex: The position where to insert this item at
1671 *
1672 * Moves the actor @inMenuItem to position @inIndex at pop-up menu @self. If position
1673 * @inIndex is greater than the number of menu items in @self or is less than 0,
1674 * then the menu item actor @inMenuItem is added to end to pop-up menu.
1675 *
1676 * Return value: Returns %TRUE if moving menu item was successful, otherwise %FALSE.
1677 */
xfdashboard_popup_menu_move_item(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem,gint inIndex)1678 gboolean xfdashboard_popup_menu_move_item(XfdashboardPopupMenu *self,
1679 XfdashboardPopupMenuItem *inMenuItem,
1680 gint inIndex)
1681 {
1682 XfdashboardPopupMenuPrivate *priv;
1683
1684 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
1685 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem), FALSE);
1686
1687 priv=self->priv;
1688
1689 /* Check if menu item is really part of this pop-up menu */
1690 if(!_xfdashboard_popup_menu_contains_menu_item(self, inMenuItem))
1691 {
1692 g_warning("%s is not a child of %s and cannot be moved",
1693 G_OBJECT_TYPE_NAME(inMenuItem),
1694 G_OBJECT_TYPE_NAME(self));
1695 return(FALSE);
1696 }
1697
1698 /* Move menu item actor to new position */
1699 g_object_ref(inMenuItem);
1700 clutter_actor_remove_child(priv->itemsContainer, CLUTTER_ACTOR(inMenuItem));
1701 clutter_actor_insert_child_at_index(priv->itemsContainer, CLUTTER_ACTOR(inMenuItem), inIndex);
1702 g_object_unref(inMenuItem);
1703
1704 /* If we get here moving menu item actor was successful */
1705 return(TRUE);
1706 }
1707
1708 /**
1709 * xfdashboard_popup_menu_get_item:
1710 * @self: A #XfdashboardPopupMenu
1711 * @inIndex: The position whose menu item to get
1712 *
1713 * Returns the menu item actor at position @inIndex at pop-up menu @self.
1714 *
1715 * Return value: Returns #XfdashboardPopupMenuItem of the menu item at position @inIndex or
1716 * %NULL in case of errors or if index is out of range that means it is greater
1717 * than the number of menu items in @self or is less than 0.
1718 */
xfdashboard_popup_menu_get_item(XfdashboardPopupMenu * self,gint inIndex)1719 XfdashboardPopupMenuItem* xfdashboard_popup_menu_get_item(XfdashboardPopupMenu *self, gint inIndex)
1720 {
1721 XfdashboardPopupMenuPrivate *priv;
1722 ClutterActor *menuItem;
1723
1724 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), NULL);
1725 g_return_val_if_fail(inIndex>=0 && inIndex<clutter_actor_get_n_children(self->priv->itemsContainer), NULL);
1726
1727 priv=self->priv;
1728
1729 /* Get and return child at requested position at items container */
1730 menuItem=clutter_actor_get_child_at_index(priv->itemsContainer, inIndex);
1731 return(XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
1732 }
1733
1734 /**
1735 * xfdashboard_popup_menu_get_item_index:
1736 * @self: A #XfdashboardPopupMenu
1737 * @inMenuItem: The #XfdashboardPopupMenuItem menu item whose index to lookup
1738 *
1739 * Returns the position for menu item actor @inMenuItem of pop-up menu @self.
1740 *
1741 * Return value: Returns the position of the menu item or -1 in case of errors
1742 * or if pop-up menu does not have the menu item
1743 */
xfdashboard_popup_menu_get_item_index(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem)1744 gint xfdashboard_popup_menu_get_item_index(XfdashboardPopupMenu *self, XfdashboardPopupMenuItem *inMenuItem)
1745 {
1746 XfdashboardPopupMenuPrivate *priv;
1747 gint index;
1748 ClutterActorIter iter;
1749 ClutterActor *child;
1750
1751 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), -1);
1752 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem), -1);
1753
1754 priv=self->priv;
1755
1756 /* Iterate through menu item and return the index if the requested one was found */
1757 index=0;
1758
1759 clutter_actor_iter_init(&iter, priv->itemsContainer);
1760 while(clutter_actor_iter_next(&iter, &child))
1761 {
1762 /* If this child is the one we are looking for return index now */
1763 if(child==CLUTTER_ACTOR(inMenuItem)) return(index);
1764
1765 /* Increase index */
1766 index++;
1767 }
1768
1769 /* If we get here we did not find the requested menu item */
1770 return(-1);
1771 }
1772
1773 /**
1774 * xfdashboard_popup_menu_remove_item:
1775 * @self: A #XfdashboardPopupMenu
1776 * @inMenuItem: A #XfdashboardPopupMenuItem menu item to remove
1777 *
1778 * Removes the actor @inMenuItem from pop-up menu @self. When the pop-up menu holds
1779 * the last reference on that menu item actor then it will be destroyed otherwise
1780 * it will only be removed from pop-up menu.
1781 *
1782 * If the removed menu item actor implements the #XfdashboardStylable interface
1783 * the CSS class popup-menu-item will be removed also.
1784 *
1785 * Return value: Returns %TRUE if moving menu item was successful, otherwise %FALSE.
1786 */
xfdashboard_popup_menu_remove_item(XfdashboardPopupMenu * self,XfdashboardPopupMenuItem * inMenuItem)1787 gboolean xfdashboard_popup_menu_remove_item(XfdashboardPopupMenu *self, XfdashboardPopupMenuItem *inMenuItem)
1788 {
1789 XfdashboardPopupMenuPrivate *priv;
1790
1791 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU(self), FALSE);
1792 g_return_val_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem), FALSE);
1793
1794 priv=self->priv;
1795
1796 /* Check if menu item is really part of this pop-up menu */
1797 if(!_xfdashboard_popup_menu_contains_menu_item(self, inMenuItem))
1798 {
1799 g_warning("%s is not a child of %s and cannot be removed",
1800 G_OBJECT_TYPE_NAME(inMenuItem),
1801 G_OBJECT_TYPE_NAME(self));
1802 return(FALSE);
1803 }
1804
1805 /* Take extra reference on actor to remove to keep it alive while working with it */
1806 g_object_ref(inMenuItem);
1807
1808 /* Remove CSS class 'popup-menu-item' from menu item going to be removed */
1809 if(XFDASHBOARD_IS_STYLABLE(inMenuItem))
1810 {
1811 xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(inMenuItem), "popup-menu-item");
1812 }
1813
1814 /* Remove menu item actor from pop-up menu */
1815 clutter_actor_remove_child(priv->itemsContainer, CLUTTER_ACTOR(inMenuItem));
1816
1817 /* Disconnect signal handlers from removed menu item */
1818 g_signal_handlers_disconnect_by_func(inMenuItem, G_CALLBACK(_xfdashboard_popup_menu_on_menu_item_activated), self);
1819
1820 /* Emit signal */
1821 g_signal_emit(self, XfdashboardPopupMenuSignals[SIGNAL_ITEM_REMOVED], 0, inMenuItem);
1822
1823 /* Release extra reference on actor to took to keep it alive */
1824 g_object_unref(inMenuItem);
1825
1826 /* If we get here we removed the menu item actor successfully */
1827 return(TRUE);
1828 }
1829
1830 /**
1831 * xfdashboard_popup_menu_activate:
1832 * @self: A #XfdashboardPopupMenu
1833 *
1834 * Displays the pop-up menu at @self and makes it available for selection.
1835 *
1836 * This actor will gain the focus automatically and will select the first menu item.
1837 */
xfdashboard_popup_menu_activate(XfdashboardPopupMenu * self)1838 void xfdashboard_popup_menu_activate(XfdashboardPopupMenu *self)
1839 {
1840 XfdashboardPopupMenuPrivate *priv;
1841 GdkDisplay *display;
1842 #if GTK_CHECK_VERSION(3, 20, 0)
1843 GdkSeat *seat;
1844 #else
1845 GdkDeviceManager *deviceManager;
1846 #endif
1847 GdkDevice *pointerDevice;
1848 gint pointerX, pointerY;
1849 XfdashboardWindowTrackerMonitor *monitor;
1850 gint monitorX, monitorY, monitorWidth, monitorHeight;
1851 gfloat x, y, w, h;
1852
1853 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1854
1855 priv=self->priv;
1856
1857 /* If this actor is already active, then do nothing */
1858 if(priv->isActive) return;
1859
1860 /* Move popup menu next to pointer similar to tooltips but keep it on current monitor */
1861 display=gdk_display_get_default();
1862 #if GTK_CHECK_VERSION(3, 20, 0)
1863 seat=gdk_display_get_default_seat(display);
1864 pointerDevice=gdk_seat_get_pointer(seat);
1865 #else
1866 deviceManager=gdk_display_get_device_manager(display);
1867 pointerDevice=gdk_device_manager_get_client_pointer(deviceManager);
1868 #endif
1869 gdk_device_get_position(pointerDevice, NULL, &pointerX, &pointerY);
1870 XFDASHBOARD_DEBUG(self, ACTOR,
1871 "Pointer is at position %d,%d",
1872 pointerX, pointerY);
1873
1874 monitor=xfdashboard_window_tracker_get_monitor_by_position(priv->windowTracker, pointerX, pointerY);
1875 if(!monitor)
1876 {
1877 /* Show error message */
1878 g_critical("Could not find monitor at pointer position %d,%d",
1879 pointerX,
1880 pointerY);
1881
1882 return;
1883 }
1884
1885 xfdashboard_window_tracker_monitor_get_geometry(monitor, &monitorX, &monitorY, &monitorWidth, &monitorHeight);
1886 XFDASHBOARD_DEBUG(self, ACTOR,
1887 "Pointer is on monitor %d with position at %d,%d and size of %dx%d",
1888 xfdashboard_window_tracker_monitor_get_number(monitor),
1889 monitorX, monitorY,
1890 monitorWidth, monitorHeight);
1891
1892 x=pointerX;
1893 y=pointerY;
1894 clutter_actor_get_size(CLUTTER_ACTOR(self), &w, &h);
1895 if(x<monitorX) x=monitorX;
1896 if((x+w)>=(monitorX+monitorWidth)) x=(monitorX+monitorWidth)-w;
1897 if(y<monitorY) y=monitorY;
1898 if((y+h)>=(monitorY+monitorHeight)) y=(monitorY+monitorHeight)-h;
1899 clutter_actor_set_position(CLUTTER_ACTOR(self), floor(x), floor(y));
1900
1901 /* Now start capturing event in "capture" phase to stop propagating event to
1902 * other actors except this one while popup menu is active.
1903 */
1904 priv->capturedEventSignalID=
1905 g_signal_connect_swapped(priv->stage,
1906 "captured-event",
1907 G_CALLBACK(_xfdashboard_popup_menu_on_captured_event),
1908 self);
1909
1910 /* Show popup menu */
1911 clutter_actor_show(CLUTTER_ACTOR(self));
1912
1913 /* Set flag that this pop-up menu is now active otherwise we cannot focus
1914 * this actor.
1915 */
1916 priv->isActive=TRUE;
1917
1918 /* Make popup menu focusable as this also marks this actor to be active */
1919 xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), TRUE);
1920
1921 /* Move focus to popup menu but remember the actor which has current focus */
1922 priv->oldFocusable=xfdashboard_focus_manager_get_focus(priv->focusManager);
1923 if(priv->oldFocusable) g_object_add_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
1924
1925 xfdashboard_focus_manager_set_focus(priv->focusManager, XFDASHBOARD_FOCUSABLE(self));
1926 }
1927
1928 /**
1929 * xfdashboard_popup_menu_cancel:
1930 * @self: A #XfdashboardPopupMenu
1931 *
1932 * Hides the pop-up menu if displayed and stops making it available for selection.
1933 *
1934 * The actor tries to refocus the actor which had the focus before this pop-up
1935 * menu was displayed. If that actor cannot be focused it move the focus to the
1936 * next focusable actor.
1937 */
xfdashboard_popup_menu_cancel(XfdashboardPopupMenu * self)1938 void xfdashboard_popup_menu_cancel(XfdashboardPopupMenu *self)
1939 {
1940 XfdashboardPopupMenuPrivate *priv;
1941
1942 g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU(self));
1943
1944 priv=self->priv;
1945
1946 /* Do nothing if pop-up menu is not active */
1947 if(!priv->isActive) return;
1948
1949 /* Unset flag that pop-up menu is active to prevent recursive calls on this
1950 * function, e.g. if pop-up menu is cancelled because the object instance
1951 * is disposed.
1952 */
1953 priv->isActive=FALSE;
1954
1955 /* Stop capturing events in "capture" phase as this popup menu actor will not
1956 * be active anymore.
1957 */
1958 if(priv->capturedEventSignalID)
1959 {
1960 g_signal_handler_disconnect(priv->stage, priv->capturedEventSignalID);
1961 priv->capturedEventSignalID=0;
1962 }
1963
1964 /* Move focus to actor which had the focus previously */
1965 if(priv->oldFocusable)
1966 {
1967 /* Remove weak pointer from previously focused actor */
1968 g_object_remove_weak_pointer(G_OBJECT(priv->oldFocusable), &priv->oldFocusable);
1969
1970 /* Move focus to previous focussed actor */
1971 xfdashboard_focus_manager_set_focus(priv->focusManager, priv->oldFocusable);
1972
1973 /* Forget previous focussed actor */
1974 priv->oldFocusable=NULL;
1975 }
1976
1977 /* Hide popup menu */
1978 clutter_actor_hide(CLUTTER_ACTOR(self));
1979
1980 /* Reset popup menu to be not focusable as this also marks this actor is
1981 * not active anymore.
1982 */
1983 xfdashboard_actor_set_can_focus(XFDASHBOARD_ACTOR(self), FALSE);
1984
1985 /* Destroy this pop-up menu actor when destroy-on-cancel was enabled */
1986 if(priv->destroyOnCancel)
1987 {
1988 xfdashboard_actor_destroy(CLUTTER_ACTOR(self));
1989 }
1990 }
1991