1 /*
2  * quicklaunch: Quicklaunch box
3  *
4  * Copyright 2012-2020 Stephan Haller <nomad@froevel.de>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  * MA 02110-1301, USA.
20  *
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 
29 #include <libxfdashboard/quicklaunch.h>
30 
31 #include <glib/gi18n-lib.h>
32 #include <math.h>
33 #include <gtk/gtk.h>
34 
35 #include <libxfdashboard/enums.h>
36 #include <libxfdashboard/application.h>
37 #include <libxfdashboard/application-button.h>
38 #include <libxfdashboard/toggle-button.h>
39 #include <libxfdashboard/drag-action.h>
40 #include <libxfdashboard/drop-action.h>
41 #include <libxfdashboard/applications-view.h>
42 #include <libxfdashboard/tooltip-action.h>
43 #include <libxfdashboard/focusable.h>
44 #include <libxfdashboard/marshal.h>
45 #include <libxfdashboard/desktop-app-info.h>
46 #include <libxfdashboard/desktop-app-info-action.h>
47 #include <libxfdashboard/application-database.h>
48 #include <libxfdashboard/application-tracker.h>
49 #include <libxfdashboard/window-tracker.h>
50 #include <libxfdashboard/window-tracker-window.h>
51 #include <libxfdashboard/click-action.h>
52 #include <libxfdashboard/popup-menu.h>
53 #include <libxfdashboard/popup-menu-item-button.h>
54 #include <libxfdashboard/popup-menu-item-separator.h>
55 #include <libxfdashboard/utils.h>
56 #include <libxfdashboard/compat.h>
57 #include <libxfdashboard/debug.h>
58 
59 
60 /* Define this class in GObject system */
61 static void _xfdashboard_quicklaunch_focusable_iface_init(XfdashboardFocusableInterface *iface);
62 
63 struct _XfdashboardQuicklaunchPrivate
64 {
65 	/* Properties related */
66 	GPtrArray						*favourites;
67 
68 	gfloat							normalIconSize;
69 	gfloat							scaleMin;
70 	gfloat							scaleMax;
71 	gfloat							scaleStep;
72 
73 	gfloat							spacing;
74 
75 	ClutterOrientation				orientation;
76 
77 	/* Instance related */
78 	XfconfChannel					*xfconfChannel;
79 	guint							xfconfFavouritesBindingID;
80 
81 	gfloat							scaleCurrent;
82 
83 	ClutterActor					*appsButton;
84 	ClutterActor					*trashButton;
85 
86 	guint							dragMode;
87 	ClutterActor					*dragPreviewIcon;
88 
89 	ClutterActor					*selectedItem;
90 
91 	ClutterActor					*separatorFavouritesToDynamic;
92 
93 	XfdashboardApplicationDatabase	*appDB;
94 	XfdashboardApplicationTracker	*appTracker;
95 };
96 
97 G_DEFINE_TYPE_WITH_CODE(XfdashboardQuicklaunch,
98 						xfdashboard_quicklaunch,
99 						XFDASHBOARD_TYPE_BACKGROUND,
100 						G_ADD_PRIVATE(XfdashboardQuicklaunch)
101 						G_IMPLEMENT_INTERFACE(XFDASHBOARD_TYPE_FOCUSABLE, _xfdashboard_quicklaunch_focusable_iface_init))
102 
103 /* Properties */
104 enum
105 {
106 	PROP_0,
107 
108 	PROP_FAVOURITES,
109 	PROP_NORMAL_ICON_SIZE,
110 	PROP_SPACING,
111 	PROP_ORIENTATION,
112 
113 	PROP_LAST
114 };
115 
116 static GParamSpec* XfdashboardQuicklaunchProperties[PROP_LAST]={ 0, };
117 
118 /* Signals */
119 enum
120 {
121 	SIGNAL_FAVOURITE_ADDED,
122 	SIGNAL_FAVOURITE_REMOVED,
123 
124 	ACTION_SELECTION_ADD_FAVOURITE,
125 	ACTION_SELECTION_REMOVE_FAVOURITE,
126 	ACTION_FAVOURITE_REORDER_LEFT,
127 	ACTION_FAVOURITE_REORDER_RIGHT,
128 	ACTION_FAVOURITE_REORDER_UP,
129 	ACTION_FAVOURITE_REORDER_DOWN,
130 
131 	SIGNAL_LAST
132 };
133 
134 static guint XfdashboardQuicklaunchSignals[SIGNAL_LAST]={ 0, };
135 
136 
137 /* IMPLEMENTATION: Private variables and methods */
138 #define FAVOURITES_XFCONF_PROP				"/favourites"
139 
140 #define LAUNCH_NEW_INSTANCE_XFCONF_PROP		"/always-launch-new-instance"
141 #define DEFAULT_LAUNCH_NEW_INSTANCE			TRUE
142 
143 #define DEFAULT_SCALE_MIN			0.1
144 #define DEFAULT_SCALE_MAX			1.0
145 #define DEFAULT_SCALE_STEP			0.1
146 
147 enum
148 {
149 	DRAG_MODE_NONE,
150 	DRAG_MODE_CREATE,
151 	DRAG_MODE_MOVE_EXISTING
152 };
153 
154 /* Forward declarations */
155 static void _xfdashboard_quicklaunch_update_property_from_icons(XfdashboardQuicklaunch *self);
156 static ClutterActor* _xfdashboard_quicklaunch_create_dynamic_actor(XfdashboardQuicklaunch *self, GAppInfo *inAppInfo);
157 static ClutterActor* _xfdashboard_quicklaunch_create_favourite_actor(XfdashboardQuicklaunch *self, GAppInfo *inAppInfo);
158 
159 /* Get actor for desktop application information */
_xfdashboard_quicklaunch_get_actor_for_appinfo(XfdashboardQuicklaunch * self,GAppInfo * inAppInfo)160 static ClutterActor* _xfdashboard_quicklaunch_get_actor_for_appinfo(XfdashboardQuicklaunch *self,
161 																	GAppInfo *inAppInfo)
162 {
163 	XfdashboardQuicklaunchPrivate	*priv;
164 	ClutterActorIter				iter;
165 	ClutterActor					*child;
166 	GAppInfo						*desktopAppInfo;
167 	GFile							*desktopFile;
168 
169 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
170 	g_return_val_if_fail(G_IS_APP_INFO(inAppInfo), NULL);
171 
172 	priv=self->priv;
173 
174 	/* If requested application information does not contain a desktop file
175 	 * (means it must derive from XfdashboardDesktopAppInfo) then assume
176 	 * no actor exists for it.
177 	 */
178 	if(!XFDASHBOARD_IS_DESKTOP_APP_INFO(inAppInfo))
179 	{
180 		XFDASHBOARD_DEBUG(self, ACTOR,
181 							"%s is derived from %s but not derived %s",
182 							G_OBJECT_TYPE_NAME(inAppInfo),
183 							g_type_name(G_TYPE_APP_INFO),
184 							g_type_name(XFDASHBOARD_TYPE_DESKTOP_APP_INFO));
185 		return(NULL);
186 	}
187 
188 	/* Check if application information is valid and provides a desktop file */
189 	desktopFile=xfdashboard_desktop_app_info_get_file(XFDASHBOARD_DESKTOP_APP_INFO(inAppInfo));
190 	if(!desktopFile)
191 	{
192 		g_critical("Could not check for duplicates for invalid %s object so assume no actor exists",
193 					G_OBJECT_TYPE_NAME(inAppInfo));
194 		return(NULL);
195 	}
196 
197 	/* Iterate through actors and check each application button if it
198 	 * provides the requested desktop file of application information.
199 	 */
200 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
201 	while(clutter_actor_iter_next(&iter, &child))
202 	{
203 		/* Only check application buttons */
204  		if(!XFDASHBOARD_IS_APPLICATION_BUTTON(child)) continue;
205 
206 		/* Do not check preview icon for drag if available */
207 		if(priv->dragPreviewIcon && child==priv->dragPreviewIcon) continue;
208 
209 		/* Check if application button provides requested desktop file */
210 		desktopAppInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(child));
211 		if(desktopAppInfo &&
212 			g_app_info_equal(desktopAppInfo, inAppInfo))
213 		{
214 			/* Found actor so return it */
215 			return(child);
216 		}
217 	}
218 
219 	/* If we get here then this quicklaunch does not have any item
220 	 * which matches the requested application information so return NULL.
221 	 */
222 	return(NULL);
223 }
224 
225 /* Check for duplicate application buttons */
_xfdashboard_quicklaunch_has_favourite_appinfo(XfdashboardQuicklaunch * self,GAppInfo * inAppInfo)226 static gboolean _xfdashboard_quicklaunch_has_favourite_appinfo(XfdashboardQuicklaunch *self, GAppInfo *inAppInfo)
227 {
228 	XfdashboardQuicklaunchPrivate	*priv;
229 	guint							i;
230 	GFile							*desktopFile;
231 
232 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), TRUE);
233 	g_return_val_if_fail(G_IS_APP_INFO(inAppInfo), TRUE);
234 
235 	priv=self->priv;
236 
237 	/* If requested application information does not contain a desktop file
238 	 * (means it must derive from XfdashboardDesktopAppInfo) then assume
239 	 * it exists already.
240 	 */
241 	if(!XFDASHBOARD_IS_DESKTOP_APP_INFO(inAppInfo))
242 	{
243 		XFDASHBOARD_DEBUG(self, ACTOR,
244 							"%s is derived from %s but not derived %s",
245 							G_OBJECT_TYPE_NAME(inAppInfo),
246 							g_type_name(G_TYPE_APP_INFO),
247 							g_type_name(XFDASHBOARD_TYPE_DESKTOP_APP_INFO));
248 		return(TRUE);
249 	}
250 
251 	/* Check if application information is valid and provides a desktop file */
252 	desktopFile=xfdashboard_desktop_app_info_get_file(XFDASHBOARD_DESKTOP_APP_INFO(inAppInfo));
253 	if(!desktopFile)
254 	{
255 		g_critical("Could not check for duplicates for invalid %s object so assume it exists",
256 					G_OBJECT_TYPE_NAME(inAppInfo));
257 		return(TRUE);
258 	}
259 
260 	/* Iterate through favourites and check if already a favourite for
261 	 * requested desktop file.
262 	 */
263 	for(i=0; i<priv->favourites->len; i++)
264 	{
265 		GValue						*value;
266 		GAppInfo					*valueAppInfo;
267 		const gchar					*valueAppInfoFilename;
268 
269 		valueAppInfo=NULL;
270 
271 		/* Get favourite value and the string it contains */
272 		value=(GValue*)g_ptr_array_index(priv->favourites, i);
273 		if(value)
274 		{
275 #if DEBUG
276 			if(!G_VALUE_HOLDS_STRING(value))
277 			{
278 				g_critical("Value at %p of type %s is not a %s so assume this desktop application item exists",
279 							value,
280 							G_VALUE_TYPE_NAME(value),
281 							g_type_name(G_TYPE_STRING));
282 				return(TRUE);
283 			}
284 #endif
285 
286 			/* Get application information for string */
287 			valueAppInfoFilename=g_value_get_string(value);
288 			if(g_path_is_absolute(valueAppInfoFilename))
289 			{
290 				valueAppInfo=xfdashboard_desktop_app_info_new_from_path(valueAppInfoFilename);
291 			}
292 				else
293 				{
294 					valueAppInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDB, valueAppInfoFilename);
295 				}
296 		}
297 
298 		/* Check if favourite value matches application information */
299 		if(valueAppInfo &&
300 			g_app_info_equal(valueAppInfo, inAppInfo))
301 		{
302 
303 			/* Release allocated resources */
304 			if(valueAppInfo) g_object_unref(valueAppInfo);
305 
306 			return(TRUE);
307 		}
308 
309 		/* Release allocated resources */
310 		if(valueAppInfo) g_object_unref(valueAppInfo);
311 	}
312 
313 	/* If we get here then this quicklaunch does not have any item
314 	 * which matches the requested application information so return FALSE.
315 	 */
316 	return(FALSE);
317 }
318 
319 /* An application icon (favourite) in quicklaunch was clicked */
_xfdashboard_quicklaunch_on_favourite_clicked(XfdashboardQuicklaunch * self,gpointer inUserData)320 static void _xfdashboard_quicklaunch_on_favourite_clicked(XfdashboardQuicklaunch *self, gpointer inUserData)
321 {
322 	XfdashboardQuicklaunchPrivate			*priv;
323 	XfdashboardApplicationButton			*button;
324 	gboolean								launchNewInstance;
325 
326 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
327 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inUserData));
328 
329 	priv=self->priv;
330 	button=XFDASHBOARD_APPLICATION_BUTTON(inUserData);
331 
332 	/* If user wants to activate the last active windows for a running instance
333 	 * of application whose button was clicked, then check if a window exists
334 	 * and activate it. Otherwise launch a new instance.
335 	 */
336 	launchNewInstance=xfconf_channel_get_bool(xfdashboard_application_get_xfconf_channel(NULL),
337 												LAUNCH_NEW_INSTANCE_XFCONF_PROP,
338 												DEFAULT_LAUNCH_NEW_INSTANCE);
339 	if(!launchNewInstance)
340 	{
341 		GAppInfo							*appInfo;
342 		const GList							*windows;
343 		XfdashboardWindowTrackerWindow		*lastActiveWindow;
344 		XfdashboardWindowTrackerWorkspace	*lastActiveWorkspace;
345 
346 		/* Get application information of application button */
347 		appInfo=xfdashboard_application_button_get_app_info(button);
348 		if(!appInfo)
349 		{
350 			xfdashboard_notify(CLUTTER_ACTOR(self),
351 								"dialog-error",
352 								_("Launching application '%s' failed: %s"),
353 								xfdashboard_application_button_get_display_name(button),
354 								_("No information available for application"));
355 
356 			g_warning("Launching application '%s' failed: %s",
357 						xfdashboard_application_button_get_display_name(button),
358 						"No information available for application");
359 
360 			return;
361 		}
362 
363 		/* Get list of windows for application */
364 		windows=xfdashboard_application_tracker_get_window_list_by_app_info(priv->appTracker, appInfo);
365 		if(windows)
366 		{
367 			/* Get last active window for application which is the first one in list */
368 			lastActiveWindow=XFDASHBOARD_WINDOW_TRACKER_WINDOW(windows->data);
369 
370 			/* If last active window for application if available, wwitch to workspace
371 			 * where it is placed at and activate it.
372 			 */
373 			if(lastActiveWindow)
374 			{
375 				/* Switch to workspace where window is placed at */
376 				lastActiveWorkspace=xfdashboard_window_tracker_window_get_workspace(lastActiveWindow);
377 				xfdashboard_window_tracker_workspace_activate(lastActiveWorkspace);
378 
379 				/* Activate window */
380 				xfdashboard_window_tracker_window_activate(lastActiveWindow);
381 
382 				/* Activating last active window of application seems to be successfully
383 				 * so quit application.
384 				 */
385 				xfdashboard_application_suspend_or_quit(NULL);
386 
387 				return;
388 			}
389 		}
390 
391 		/* If we get here we found the application but no active window,
392 		 * so check if application is running. If it is display a warning
393 		 * message as notification and return from this function. If it
394 		 * is not running, continue to start a new instance.
395 		 */
396 		if(xfdashboard_application_tracker_is_running_by_app_info(priv->appTracker, appInfo))
397 		{
398 			xfdashboard_notify(CLUTTER_ACTOR(self),
399 								"dialog-error",
400 								_("Launching application '%s' failed: %s"),
401 								xfdashboard_application_button_get_display_name(button),
402 								_("No windows to activate for application"));
403 
404 			g_warning("Launching application '%s' failed: %s",
405 						xfdashboard_application_button_get_display_name(button),
406 						"No windows to activate for application");
407 
408 			return;
409 		}
410 	}
411 
412 	/* Launch a new instance of application whose button was clicked */
413 	if(xfdashboard_application_button_execute(button, NULL))
414 	{
415 		/* Launching application seems to be successfully so quit application */
416 		xfdashboard_application_suspend_or_quit(NULL);
417 
418 		return;
419 	}
420 }
421 
422 /* User selected to open a new window or to launch that application at pop-up menu */
_xfdashboard_quicklaunch_on_favourite_popup_menu_item_launch(XfdashboardPopupMenuItem * inMenuItem,gpointer inUserData)423 static void _xfdashboard_quicklaunch_on_favourite_popup_menu_item_launch(XfdashboardPopupMenuItem *inMenuItem,
424 																			gpointer inUserData)
425 {
426 	GAppInfo							*appInfo;
427 	XfdashboardApplicationTracker		*appTracker;
428 	GIcon								*gicon;
429 	const gchar							*iconName;
430 
431 	g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem));
432 	g_return_if_fail(G_IS_APP_INFO(inUserData));
433 
434 	appInfo=G_APP_INFO(inUserData);
435 	iconName=NULL;
436 
437 	/* Get icon of application */
438 	gicon=g_app_info_get_icon(appInfo);
439 	if(gicon) iconName=g_icon_to_string(gicon);
440 
441 	/* Check if we should launch that application or to open a new window */
442 	appTracker=xfdashboard_application_tracker_get_default();
443 	if(!xfdashboard_application_tracker_is_running_by_app_info(appTracker, appInfo))
444 	{
445 		GAppLaunchContext			*context;
446 		GError						*error;
447 
448 		/* Create context to start application at */
449 		context=xfdashboard_create_app_context(NULL);
450 
451 		/* Try to launch application */
452 		error=NULL;
453 		if(!g_app_info_launch(appInfo, NULL, context, &error))
454 		{
455 			/* Show notification about failed application launch */
456 			xfdashboard_notify(CLUTTER_ACTOR(inMenuItem),
457 								iconName,
458 								_("Launching application '%s' failed: %s"),
459 								g_app_info_get_display_name(appInfo),
460 								(error && error->message) ? error->message : _("unknown error"));
461 			g_warning("Launching application '%s' failed: %s",
462 						g_app_info_get_display_name(appInfo),
463 						(error && error->message) ? error->message : "unknown error");
464 			if(error) g_error_free(error);
465 		}
466 			else
467 			{
468 				/* Show notification about successful application launch */
469 				xfdashboard_notify(CLUTTER_ACTOR(inMenuItem),
470 									iconName,
471 									_("Application '%s' launched"),
472 									g_app_info_get_display_name(appInfo));
473 
474 				/* Emit signal for successful application launch */
475 				g_signal_emit_by_name(xfdashboard_application_get_default(), "application-launched", appInfo);
476 
477 				/* Quit application */
478 				xfdashboard_application_suspend_or_quit(NULL);
479 			}
480 
481 		/* Release allocated resources */
482 		g_object_unref(context);
483 	}
484 
485 	/* Release allocated resources */
486 	g_object_unref(appTracker);
487 	g_object_unref(gicon);
488 }
489 
490 /* User selected to remove application from favourites via pop-up menu */
_xfdashboard_quicklaunch_on_favourite_popup_menu_item_remove_from_favourite(XfdashboardPopupMenuItem * inMenuItem,gpointer inUserData)491 static void _xfdashboard_quicklaunch_on_favourite_popup_menu_item_remove_from_favourite(XfdashboardPopupMenuItem *inMenuItem,
492 																						gpointer inUserData)
493 {
494 	XfdashboardApplicationButton		*appButton;
495 	GAppInfo							*appInfo;
496 	ClutterActor						*actor;
497 	XfdashboardQuicklaunch				*self;
498 	XfdashboardQuicklaunchPrivate		*priv;
499 
500 	g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem));
501 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inUserData));
502 
503 	appButton=XFDASHBOARD_APPLICATION_BUTTON(inUserData);
504 
505 	/* Find quicklaunch for application button */
506 	actor=CLUTTER_ACTOR(appButton);
507 	do
508 	{
509 		actor=clutter_actor_get_parent(actor);
510 	}
511 	while(actor && !XFDASHBOARD_IS_QUICKLAUNCH(actor));
512 
513 	if(!actor)
514 	{
515 		g_critical("Cannot find quicklaunch for application button.");
516 		return;
517 	}
518 
519 	self=XFDASHBOARD_QUICKLAUNCH(actor);
520 	priv=self->priv;
521 
522 	/* Notify about removal of favourite icon */
523 	xfdashboard_notify(CLUTTER_ACTOR(self),
524 						xfdashboard_application_button_get_icon_name(appButton),
525 						_("Favourite '%s' removed"),
526 						xfdashboard_application_button_get_display_name(appButton));
527 
528 	/* Emit signal and re-add removed favourite as dynamically added
529 	 * application button for non-favourites apps when it is still running.
530 	 */
531 	appInfo=xfdashboard_application_button_get_app_info(appButton);
532 	if(appInfo)
533 	{
534 		/* Emit signal */
535 		g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_REMOVED], 0, appInfo);
536 
537 		/* Re-add removed favourite as dynamically added application button
538 		 * for non-favourites apps when it is still running.
539 		 */
540 		if(xfdashboard_application_tracker_is_running_by_app_info(priv->appTracker, appInfo))
541 		{
542 			ClutterActor				*newAppButton;
543 
544 			newAppButton=_xfdashboard_quicklaunch_create_dynamic_actor(self, appInfo);
545 			clutter_actor_show(newAppButton);
546 			clutter_actor_add_child(CLUTTER_ACTOR(self), newAppButton);
547 		}
548 	}
549 
550 	/* Destroy favourite icon before updating property */
551 	xfdashboard_actor_destroy(CLUTTER_ACTOR(appButton));
552 
553 	/* Update favourites from icon order */
554 	_xfdashboard_quicklaunch_update_property_from_icons(self);
555 }
556 
557 /* User selected to add application to favourites via pop-up menu */
_xfdashboard_quicklaunch_on_favourite_popup_menu_item_add_to_favourite(XfdashboardPopupMenuItem * inMenuItem,gpointer inUserData)558 static void _xfdashboard_quicklaunch_on_favourite_popup_menu_item_add_to_favourite(XfdashboardPopupMenuItem *inMenuItem,
559 																					gpointer inUserData)
560 {
561 	XfdashboardApplicationButton		*appButton;
562 	GAppInfo							*appInfo;
563 	ClutterActor						*actor;
564 	XfdashboardQuicklaunch				*self;
565 	XfdashboardQuicklaunchPrivate		*priv;
566 
567 	g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM(inMenuItem));
568 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inUserData));
569 
570 	appButton=XFDASHBOARD_APPLICATION_BUTTON(inUserData);
571 
572 	/* Find quicklaunch for application button */
573 	actor=CLUTTER_ACTOR(appButton);
574 	do
575 	{
576 		actor=clutter_actor_get_parent(actor);
577 	}
578 	while(actor && !XFDASHBOARD_IS_QUICKLAUNCH(actor));
579 
580 	if(!actor)
581 	{
582 		g_critical("Cannot find quicklaunch for application button.");
583 		return;
584 	}
585 
586 	self=XFDASHBOARD_QUICKLAUNCH(actor);
587 	priv=self->priv;
588 
589 	/* Check if application button provides all information needed to add favourite
590 	 * and also check for duplicates.
591 	 */
592 	appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(appButton));
593 	if(appInfo &&
594 		!_xfdashboard_quicklaunch_has_favourite_appinfo(self, appInfo))
595 	{
596 		ClutterActor					*favouriteActor;
597 
598 		/* If an actor for current selection to add to favourites already exists,
599 		 * destroy and remove it regardless if it an actor or a favourite app or
600 		 * dynamic non-favourite app. It will be re-added later.
601 		 */
602 		favouriteActor=_xfdashboard_quicklaunch_get_actor_for_appinfo(self, appInfo);
603 		if(favouriteActor)
604 		{
605 			/* Remove existing actor from this quicklaunch */
606 			xfdashboard_actor_destroy(favouriteActor);
607 		}
608 
609 		/* Now (re-)add current selection to favourites but hidden as
610 		 * it will become visible and properly set up when function
611 		 * _xfdashboard_quicklaunch_update_property_from_icons is called.
612 		 */
613 		favouriteActor=xfdashboard_application_button_new_from_app_info(appInfo);
614 		clutter_actor_hide(favouriteActor);
615 		xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(favouriteActor), "favourite-app");
616 		clutter_actor_insert_child_below(CLUTTER_ACTOR(self), favouriteActor, priv->separatorFavouritesToDynamic);
617 
618 		/* Update favourites from icon order */
619 		_xfdashboard_quicklaunch_update_property_from_icons(self);
620 
621 		/* Notify about new favourite */
622 		xfdashboard_notify(CLUTTER_ACTOR(self),
623 							xfdashboard_application_button_get_icon_name(XFDASHBOARD_APPLICATION_BUTTON(favouriteActor)),
624 							_("Favourite '%s' added"),
625 							xfdashboard_application_button_get_display_name(XFDASHBOARD_APPLICATION_BUTTON(favouriteActor)));
626 
627 		g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_ADDED], 0, appInfo);
628 	}
629 }
630 
631 /* A right-click might have happened on an application icon (favourite) in quicklaunch */
_xfdashboard_quicklaunch_on_favourite_popup_menu(XfdashboardQuicklaunch * self,ClutterActor * inActor,gpointer inUserData)632 static void _xfdashboard_quicklaunch_on_favourite_popup_menu(XfdashboardQuicklaunch *self,
633 																ClutterActor *inActor,
634 																gpointer inUserData)
635 {
636 	XfdashboardQuicklaunchPrivate				*priv;
637 	XfdashboardApplicationButton				*appButton;
638 	XfdashboardClickAction						*action;
639 
640 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
641 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inActor));
642 	g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inUserData));
643 
644 	priv=self->priv;
645 	appButton=XFDASHBOARD_APPLICATION_BUTTON(inActor);
646 	action=XFDASHBOARD_CLICK_ACTION(inUserData);
647 
648 	/* Check if right button was used when the application button was clicked */
649 	if(xfdashboard_click_action_get_button(action)==XFDASHBOARD_CLICK_ACTION_RIGHT_BUTTON)
650 	{
651 		ClutterActor							*popup;
652 		ClutterActor							*menuItem;
653 		GAppInfo								*appInfo;
654 
655 		/* Get app info for application button as it is needed most the time */
656 		appInfo=xfdashboard_application_button_get_app_info(appButton);
657 		if(!appInfo)
658 		{
659 			g_critical("No application information available for clicked application button.");
660 			return;
661 		}
662 
663 		/* Create pop-up menu */
664 		popup=xfdashboard_popup_menu_new_for_source(CLUTTER_ACTOR(self));
665 		xfdashboard_popup_menu_set_destroy_on_cancel(XFDASHBOARD_POPUP_MENU(popup), TRUE);
666 		xfdashboard_popup_menu_set_title(XFDASHBOARD_POPUP_MENU(popup), g_app_info_get_display_name(appInfo));
667 		xfdashboard_popup_menu_set_title_gicon(XFDASHBOARD_POPUP_MENU(popup), g_app_info_get_icon(appInfo));
668 
669 		/* Add each open window to pop-up of application */
670 		if(xfdashboard_application_button_add_popup_menu_items_for_windows(appButton, XFDASHBOARD_POPUP_MENU(popup))>0)
671 		{
672 			/* Add a separator to split windows from other actions in pop-up menu */
673 			menuItem=xfdashboard_popup_menu_item_separator_new();
674 			clutter_actor_set_x_expand(menuItem, TRUE);
675 			xfdashboard_popup_menu_add_item(XFDASHBOARD_POPUP_MENU(popup), XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
676 		}
677 
678 		/* Add menu item to launch application if it is not running */
679 		if(!xfdashboard_application_tracker_is_running_by_app_info(priv->appTracker, appInfo))
680 		{
681 			menuItem=xfdashboard_popup_menu_item_button_new();
682 			xfdashboard_label_set_text(XFDASHBOARD_LABEL(menuItem), _("Launch"));
683 			clutter_actor_set_x_expand(menuItem, TRUE);
684 			xfdashboard_popup_menu_add_item(XFDASHBOARD_POPUP_MENU(popup), XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
685 
686 			g_signal_connect(menuItem,
687 								"activated",
688 								G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_popup_menu_item_launch),
689 								appInfo);
690 		}
691 
692 		/* Add application actions */
693 		if(xfdashboard_application_button_add_popup_menu_items_for_actions(appButton, XFDASHBOARD_POPUP_MENU(popup))>0)
694 		{
695 			/* Add a separator to split windows from other actions in pop-up menu */
696 			menuItem=xfdashboard_popup_menu_item_separator_new();
697 			clutter_actor_set_x_expand(menuItem, TRUE);
698 			xfdashboard_popup_menu_add_item(XFDASHBOARD_POPUP_MENU(popup), XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
699 		}
700 
701 		/* Add "Remove from favourites" if application button is for a favourite application */
702 		if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(appButton), "favourite-app"))
703 		{
704 			menuItem=xfdashboard_popup_menu_item_button_new();
705 			xfdashboard_label_set_text(XFDASHBOARD_LABEL(menuItem), _("Remove from favourites"));
706 			clutter_actor_set_x_expand(menuItem, TRUE);
707 			xfdashboard_popup_menu_add_item(XFDASHBOARD_POPUP_MENU(popup), XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
708 
709 			g_signal_connect(menuItem,
710 								"activated",
711 								G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_popup_menu_item_remove_from_favourite),
712 								appButton);
713 		}
714 
715 		/* Add "Add to favourites" if application button is for a non-favourite application */
716 		if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(appButton), "dynamic-app"))
717 		{
718 			menuItem=xfdashboard_popup_menu_item_button_new();
719 			xfdashboard_label_set_text(XFDASHBOARD_LABEL(menuItem), _("Add to favourites"));
720 			clutter_actor_set_x_expand(menuItem, TRUE);
721 			xfdashboard_popup_menu_add_item(XFDASHBOARD_POPUP_MENU(popup), XFDASHBOARD_POPUP_MENU_ITEM(menuItem));
722 
723 			g_signal_connect(menuItem,
724 								"activated",
725 								G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_popup_menu_item_add_to_favourite),
726 								appButton);
727 		}
728 
729 		/* Activate pop-up menu */
730 		xfdashboard_popup_menu_activate(XFDASHBOARD_POPUP_MENU(popup));
731 	}
732 }
733 
734 /* Drag of a quicklaunch icon begins */
_xfdashboard_quicklaunch_on_favourite_drag_begin(ClutterDragAction * inAction,ClutterActor * inActor,gfloat inStageX,gfloat inStageY,ClutterModifierType inModifiers,gpointer inUserData)735 static void _xfdashboard_quicklaunch_on_favourite_drag_begin(ClutterDragAction *inAction,
736 																ClutterActor *inActor,
737 																gfloat inStageX,
738 																gfloat inStageY,
739 																ClutterModifierType inModifiers,
740 																gpointer inUserData)
741 {
742 	XfdashboardQuicklaunch			*self;
743 	XfdashboardQuicklaunchPrivate	*priv;
744 	ClutterActor					*dragHandle;
745 	ClutterStage					*stage;
746 	GAppInfo						*appInfo;
747 
748 	g_return_if_fail(CLUTTER_IS_DRAG_ACTION(inAction));
749 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inActor));
750 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inUserData));
751 
752 	self=XFDASHBOARD_QUICKLAUNCH(inUserData);
753 	priv=self->priv;
754 
755 	/* Prevent signal "clicked" from being emitted on dragged icon */
756 	g_signal_handlers_block_by_func(inActor, _xfdashboard_quicklaunch_on_favourite_clicked, inUserData);
757 
758 	/* Get stage */
759 	stage=CLUTTER_STAGE(clutter_actor_get_stage(inActor));
760 
761 	/* Create a clone of application icon for drag handle and hide it
762 	 * initially. It is only shown if pointer is outside of quicklaunch.
763 	 */
764 	appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(inActor));
765 
766 	dragHandle=xfdashboard_application_button_new_from_app_info(appInfo);
767 	clutter_actor_set_position(dragHandle, inStageX, inStageY);
768 	xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(dragHandle), priv->normalIconSize);
769 	xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(dragHandle), FALSE);
770 	xfdashboard_label_set_style(XFDASHBOARD_LABEL(dragHandle), XFDASHBOARD_LABEL_STYLE_ICON);
771 	clutter_actor_add_child(CLUTTER_ACTOR(stage), dragHandle);
772 
773 	clutter_drag_action_set_drag_handle(inAction, dragHandle);
774 }
775 
776 /* Drag of a quicklaunch icon ends */
_xfdashboard_quicklaunch_on_favourite_drag_end(ClutterDragAction * inAction,ClutterActor * inActor,gfloat inStageX,gfloat inStageY,ClutterModifierType inModifiers,gpointer inUserData)777 static void _xfdashboard_quicklaunch_on_favourite_drag_end(ClutterDragAction *inAction,
778 															ClutterActor *inActor,
779 															gfloat inStageX,
780 															gfloat inStageY,
781 															ClutterModifierType inModifiers,
782 															gpointer inUserData)
783 {
784 	ClutterActor					*dragHandle;
785 
786 	g_return_if_fail(CLUTTER_IS_DRAG_ACTION(inAction));
787 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inActor));
788 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inUserData));
789 
790 	/* Destroy clone of application icon used as drag handle */
791 	dragHandle=clutter_drag_action_get_drag_handle(inAction);
792 	if(dragHandle)
793 	{
794 #if CLUTTER_CHECK_VERSION(1, 14, 0)
795 		/* Only unset drag handle if not running Clutter in version
796 		 * 1.12. This prevents a critical warning message in 1.12.
797 		 * Later versions of Clutter are fixed already.
798 		 */
799 		clutter_drag_action_set_drag_handle(inAction, NULL);
800 #endif
801 		xfdashboard_actor_destroy(dragHandle);
802 	}
803 
804 	/* Allow signal "clicked" from being emitted again */
805 	g_signal_handlers_unblock_by_func(inActor, _xfdashboard_quicklaunch_on_favourite_clicked, inUserData);
806 }
807 
808 /* Drag of an actor to quicklaunch as drop target begins */
_xfdashboard_quicklaunch_on_drop_begin(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)809 static gboolean _xfdashboard_quicklaunch_on_drop_begin(XfdashboardQuicklaunch *self,
810 														XfdashboardDragAction *inDragAction,
811 														gpointer inUserData)
812 {
813 	XfdashboardQuicklaunchPrivate	*priv;
814 	ClutterActor					*dragSource;
815 	ClutterActor					*draggedActor;
816 	GAppInfo						*appInfo;
817 
818 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), FALSE);
819 	g_return_val_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction), FALSE);
820 	g_return_val_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData), FALSE);
821 
822 	priv=self->priv;
823 
824 	/* Get source where dragging started and actor being dragged */
825 	dragSource=xfdashboard_drag_action_get_source(inDragAction);
826 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
827 
828 	/* Check if we can handle dragged actor from given source */
829 	priv->dragMode=DRAG_MODE_NONE;
830 
831 	if(XFDASHBOARD_IS_QUICKLAUNCH(dragSource) &&
832 		XFDASHBOARD_IS_APPLICATION_BUTTON(draggedActor) &&
833 		xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)))
834 	{
835 		priv->dragMode=DRAG_MODE_MOVE_EXISTING;
836 	}
837 
838 	if(!XFDASHBOARD_IS_QUICKLAUNCH(dragSource) &&
839 		XFDASHBOARD_IS_APPLICATION_BUTTON(draggedActor) &&
840 		xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)))
841 	{
842 		gboolean					isExistingItem;
843 
844 		isExistingItem=FALSE;
845 
846 		/* Get application information of item which should be added */
847 		appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor));
848 		if(appInfo)
849 		{
850 			isExistingItem=_xfdashboard_quicklaunch_has_favourite_appinfo(self, appInfo);
851 			if(!isExistingItem) priv->dragMode=DRAG_MODE_CREATE;
852 		}
853 	}
854 
855 	/* Create a visible copy of dragged application button and insert it
856 	 * after dragged icon in quicklaunch. This one is the one which is
857 	 * moved within quicklaunch. It is used as preview how quicklaunch
858 	 * will look like if drop will be successful. It is also needed to
859 	 * restore original order of all favourite icons if drag was
860 	 * cancelled by just destroying this preview icon.
861 	 * If dragging an favourite icon we will hide the it and leave it
862 	 * untouched when drag is cancelled.
863 	 */
864 	if(priv->dragMode!=DRAG_MODE_NONE)
865 	{
866 		appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor));
867 
868 		priv->dragPreviewIcon=xfdashboard_application_button_new_from_app_info(appInfo);
869 		xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(priv->dragPreviewIcon), priv->normalIconSize);
870 		xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(priv->dragPreviewIcon), FALSE);
871 		xfdashboard_label_set_style(XFDASHBOARD_LABEL(priv->dragPreviewIcon), XFDASHBOARD_LABEL_STYLE_ICON);
872 		if(priv->dragMode==DRAG_MODE_CREATE) clutter_actor_hide(priv->dragPreviewIcon);
873 		clutter_actor_add_child(CLUTTER_ACTOR(self), priv->dragPreviewIcon);
874 
875 		if(priv->dragMode==DRAG_MODE_MOVE_EXISTING)
876 		{
877 			clutter_actor_set_child_below_sibling(CLUTTER_ACTOR(self),
878 													priv->dragPreviewIcon,
879 													draggedActor);
880 			clutter_actor_hide(draggedActor);
881 		}
882 	}
883 
884 	/* Hide all dynamically added application button for non-favourite apps */
885 	if(priv->dragMode!=DRAG_MODE_NONE)
886 	{
887 		ClutterActorIter			iter;
888 		ClutterActor				*child;
889 
890 		clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
891 		while(clutter_actor_iter_next(&iter, &child))
892 		{
893 			/* Only check application buttons */
894 			if(!XFDASHBOARD_IS_APPLICATION_BUTTON(child)) continue;
895 
896 			/* If actor is an application button for non-favourite apps,
897 			 * hide it now.
898 			 */
899 			if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "dynamic-app"))
900 			{
901 				clutter_actor_hide(child);
902 			}
903 		}
904 	}
905 
906 	/* If drag mode is set return TRUE because we can handle dragged actor
907 	 * otherwise return FALSE
908 	 */
909 	return(priv->dragMode!=DRAG_MODE_NONE ? TRUE : FALSE);
910 }
911 
912 /* Dragged actor was dropped on this drop target */
_xfdashboard_quicklaunch_on_drop_drop(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gfloat inX,gfloat inY,gpointer inUserData)913 static void _xfdashboard_quicklaunch_on_drop_drop(XfdashboardQuicklaunch *self,
914 													XfdashboardDragAction *inDragAction,
915 													gfloat inX,
916 													gfloat inY,
917 													gpointer inUserData)
918 {
919 	XfdashboardQuicklaunchPrivate		*priv;
920 	ClutterActor						*draggedActor;
921 	ClutterActorIter					iter;
922 	ClutterActor						*child;
923 
924 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
925 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
926 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
927 
928 	priv=self->priv;
929 
930 	/* Get dragged actor */
931 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
932 
933 	/* Remove dynamically added non-favourite application buttons and
934 	 * emit signal when a favourite icon was added.
935 	 */
936 	if(priv->dragMode==DRAG_MODE_CREATE)
937 	{
938 		GAppInfo						*appInfo;
939 		ClutterActor					*actor;
940 
941 		xfdashboard_notify(CLUTTER_ACTOR(self),
942 							xfdashboard_application_button_get_icon_name(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)),
943 							_("Favourite '%s' added"),
944 							xfdashboard_application_button_get_display_name(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)));
945 
946 		appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor));
947 		if(appInfo)
948 		{
949 			/* Remove any application button marked as dynamically added for non-favourite
950 			 * apps for the newly added favourite if available.
951 			 */
952 			actor=_xfdashboard_quicklaunch_get_actor_for_appinfo(self, appInfo);
953 			if(actor) xfdashboard_actor_destroy(actor);
954 
955 			/* Emit signal for newly added favourite */
956 			g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_ADDED], 0, appInfo);
957 		}
958 
959 		/* Set CSS class for favourite to get it included when property is updated
960 		 * in function _xfdashboard_quicklaunch_update_property_from_icons.
961 		 */
962 		xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->dragPreviewIcon), "favourite-app");
963 	}
964 
965 	/* If drag mode is reorder move originally dragged application icon
966 	 * to its final position and destroy preview application icon.
967 	 */
968 	if(priv->dragMode==DRAG_MODE_MOVE_EXISTING)
969 	{
970 		clutter_actor_set_child_below_sibling(CLUTTER_ACTOR(self),
971 												draggedActor,
972 												priv->dragPreviewIcon);
973 		clutter_actor_show(draggedActor);
974 
975 		if(priv->dragPreviewIcon)
976 		{
977 			xfdashboard_actor_destroy(priv->dragPreviewIcon);
978 			priv->dragPreviewIcon=NULL;
979 		}
980 	}
981 
982 	/* Show (remaining) hidden application buttons for non-favourite apps again */
983 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
984 	while(clutter_actor_iter_next(&iter, &child))
985 	{
986 		/* Only check application buttons */
987 		if(!XFDASHBOARD_IS_APPLICATION_BUTTON(child)) continue;
988 
989 		/* If actor is an application button for non-favourite apps,
990 		 * show it now.
991 		 */
992 		if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "dynamic-app"))
993 		{
994 			clutter_actor_show(child);
995 		}
996 	}
997 
998 	/* Update favourites from icon order */
999 	_xfdashboard_quicklaunch_update_property_from_icons(self);
1000 
1001 	/* Reset drag mode */
1002 	priv->dragMode=DRAG_MODE_NONE;
1003 }
1004 
1005 /* Drag of an actor to this drop target ended without actor being dropped here */
_xfdashboard_quicklaunch_on_drop_end(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1006 static void _xfdashboard_quicklaunch_on_drop_end(XfdashboardQuicklaunch *self,
1007 													XfdashboardDragAction *inDragAction,
1008 													gpointer inUserData)
1009 {
1010 	XfdashboardQuicklaunchPrivate		*priv;
1011 	ClutterActor						*draggedActor;
1012 	ClutterActorIter					iter;
1013 	ClutterActor						*child;
1014 
1015 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1016 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1017 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1018 
1019 	priv=self->priv;
1020 
1021 	/* Get dragged actor */
1022 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
1023 
1024 	/* Show hidden application buttons for non-favourite apps again */
1025 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1026 	while(clutter_actor_iter_next(&iter, &child))
1027 	{
1028 		/* Only check application buttons */
1029 		if(!XFDASHBOARD_IS_APPLICATION_BUTTON(child)) continue;
1030 
1031 		/* If actor is an application button for non-favourite apps,
1032 		 * show it now.
1033 		 */
1034 		if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "dynamic-app"))
1035 		{
1036 			clutter_actor_show(child);
1037 		}
1038 	}
1039 
1040 	/* Destroy preview icon and show originally dragged favourite icon.
1041 	 * Doing it this way will restore the order of favourite icons.
1042 	 */
1043 	if(priv->dragPreviewIcon)
1044 	{
1045 		xfdashboard_actor_destroy(priv->dragPreviewIcon);
1046 		priv->dragPreviewIcon=NULL;
1047 	}
1048 
1049 	if(priv->dragMode==DRAG_MODE_MOVE_EXISTING) clutter_actor_show(draggedActor);
1050 	priv->dragMode=DRAG_MODE_NONE;
1051 }
1052 
1053 /* Drag of an actor entered this drop target */
_xfdashboard_quicklaunch_on_drop_enter(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1054 static void _xfdashboard_quicklaunch_on_drop_enter(XfdashboardQuicklaunch *self,
1055 													XfdashboardDragAction *inDragAction,
1056 													gpointer inUserData)
1057 {
1058 	ClutterActor					*dragHandle;
1059 
1060 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1061 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1062 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1063 
1064 	/* Get source where dragging started and actor being dragged */
1065 	dragHandle=clutter_drag_action_get_drag_handle(CLUTTER_DRAG_ACTION(inDragAction));
1066 
1067 	/* Hide drag handle in this quicklaunch */
1068 	clutter_actor_hide(dragHandle);
1069 }
1070 
1071 /* Drag of an actor moves over this drop target */
_xfdashboard_quicklaunch_on_drop_motion(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gfloat inX,gfloat inY,gpointer inUserData)1072 static void _xfdashboard_quicklaunch_on_drop_motion(XfdashboardQuicklaunch *self,
1073 													XfdashboardDragAction *inDragAction,
1074 													gfloat inX,
1075 													gfloat inY,
1076 													gpointer inUserData)
1077 {
1078 	XfdashboardQuicklaunchPrivate	*priv;
1079 	ClutterActor					*draggedActor;
1080 	ClutterActor					*dragHandle;
1081 
1082 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1083 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1084 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1085 
1086 	priv=self->priv;
1087 
1088 	/* Get actor being dragged and the drag handle */
1089 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
1090 	dragHandle=clutter_drag_action_get_drag_handle(CLUTTER_DRAG_ACTION(inDragAction));
1091 
1092 	/* Get new position of preview application icon in quicklaunch
1093 	 * if dragged actor is an application icon
1094 	 */
1095 	if(XFDASHBOARD_IS_APPLICATION_BUTTON(draggedActor))
1096 	{
1097 		ClutterStage				*stage;
1098 		gboolean					oldPreviewReactive;
1099 		gboolean					oldHandleReactive;
1100 		gfloat						stageX, stageY;
1101 		gfloat						deltaX, deltaY;
1102 		ClutterActor				*actorUnderMouse;
1103 		ClutterActorIter			iter;
1104 		ClutterActor				*iterChild;
1105 
1106 		/* Preview icon and drag handle should not be reactive to prevent
1107 		 * clutter_stage_get_actor_at_pos() choosing one of both as the
1108 		 * actor under mouse. But remember their state to reset it later.
1109 		 */
1110 		oldPreviewReactive=clutter_actor_get_reactive(priv->dragPreviewIcon);
1111 		clutter_actor_set_reactive(priv->dragPreviewIcon, FALSE);
1112 
1113 		oldHandleReactive=clutter_actor_get_reactive(dragHandle);
1114 		clutter_actor_set_reactive(dragHandle, FALSE);
1115 
1116 		/* Get new position and move preview icon */
1117 		clutter_drag_action_get_motion_coords(CLUTTER_DRAG_ACTION(inDragAction), &stageX, &stageY);
1118 		xfdashboard_drag_action_get_motion_delta(inDragAction, &deltaX, &deltaY);
1119 
1120 		stage=CLUTTER_STAGE(clutter_actor_get_stage(dragHandle));
1121 		actorUnderMouse=clutter_stage_get_actor_at_pos(stage, CLUTTER_PICK_REACTIVE, stageX, stageY);
1122 		if(XFDASHBOARD_IS_APPLICATION_BUTTON(actorUnderMouse) &&
1123 			actorUnderMouse!=priv->dragPreviewIcon)
1124 		{
1125 			if((priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL && deltaX<0) ||
1126 				(priv->orientation==CLUTTER_ORIENTATION_VERTICAL && deltaY<0))
1127 			{
1128 				clutter_actor_set_child_below_sibling(CLUTTER_ACTOR(self),
1129 														priv->dragPreviewIcon,
1130 														actorUnderMouse);
1131 			}
1132 				else
1133 				{
1134 					clutter_actor_set_child_above_sibling(CLUTTER_ACTOR(self),
1135 															priv->dragPreviewIcon,
1136 															actorUnderMouse);
1137 				}
1138 
1139 			/* Show preview icon now if drag mode is "new". Doing it earlier will
1140 			 * show preview icon at wrong position when entering quicklaunch
1141 			 */
1142 			if(priv->dragMode==DRAG_MODE_CREATE) clutter_actor_show(priv->dragPreviewIcon);
1143 
1144 			/* Iterate through list of current actors and enable allocation animation */
1145 			clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1146 			if((priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL && deltaX<0) ||
1147 				(priv->orientation==CLUTTER_ORIENTATION_VERTICAL && deltaY<0))
1148 			{
1149 				while(clutter_actor_iter_next(&iter, &iterChild) && iterChild!=priv->dragPreviewIcon)
1150 				{
1151 					if(XFDASHBOARD_IS_ACTOR(iterChild))
1152 					{
1153 						xfdashboard_actor_enable_allocation_animation_once(XFDASHBOARD_ACTOR(iterChild));
1154 					}
1155 				}
1156 			}
1157 				else
1158 				{
1159 					while(clutter_actor_iter_next(&iter, &iterChild) && iterChild!=priv->dragPreviewIcon);
1160 					while(clutter_actor_iter_next(&iter, &iterChild))
1161 					{
1162 						if(XFDASHBOARD_IS_ACTOR(iterChild))
1163 						{
1164 							xfdashboard_actor_enable_allocation_animation_once(XFDASHBOARD_ACTOR(iterChild));
1165 						}
1166 					}
1167 				}
1168 		}
1169 
1170 		/* Reset reactive state of preview icon and drag handle */
1171 		clutter_actor_set_reactive(priv->dragPreviewIcon, oldPreviewReactive);
1172 		clutter_actor_set_reactive(dragHandle, oldHandleReactive);
1173 	}
1174 }
1175 
1176 /* Drag of an actor entered this drop target */
_xfdashboard_quicklaunch_on_drop_leave(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1177 static void _xfdashboard_quicklaunch_on_drop_leave(XfdashboardQuicklaunch *self,
1178 													XfdashboardDragAction *inDragAction,
1179 													gpointer inUserData)
1180 {
1181 	XfdashboardQuicklaunchPrivate	*priv;
1182 	ClutterActor					*dragHandle;
1183 
1184 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1185 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1186 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1187 
1188 	priv=self->priv;
1189 
1190 	/* Get source where dragging started and drag handle */
1191 	dragHandle=clutter_drag_action_get_drag_handle(CLUTTER_DRAG_ACTION(inDragAction));
1192 
1193 	/* Show drag handle outside this quicklaunch */
1194 	clutter_actor_show(dragHandle);
1195 
1196 	/* Hide preview icon if drag mode is "create new" favourite */
1197 	if(priv->dragMode==DRAG_MODE_CREATE) clutter_actor_hide(priv->dragPreviewIcon);
1198 }
1199 
1200 /* Drag of an actor to trash drop target begins */
_xfdashboard_quicklaunch_on_trash_drop_begin(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1201 static gboolean _xfdashboard_quicklaunch_on_trash_drop_begin(XfdashboardQuicklaunch *self,
1202 																XfdashboardDragAction *inDragAction,
1203 																gpointer inUserData)
1204 {
1205 	XfdashboardQuicklaunchPrivate	*priv;
1206 	ClutterActor					*dragSource;
1207 	ClutterActor					*draggedActor;
1208 
1209 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), FALSE);
1210 	g_return_val_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction), FALSE);
1211 	g_return_val_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData), FALSE);
1212 
1213 	priv=self->priv;
1214 
1215 	/* Get source where dragging started and actor being dragged */
1216 	dragSource=xfdashboard_drag_action_get_source(inDragAction);
1217 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
1218 
1219 	/* Check if we can handle dragged actor from given source */
1220 	if(XFDASHBOARD_IS_QUICKLAUNCH(dragSource) &&
1221 		XFDASHBOARD_IS_APPLICATION_BUTTON(draggedActor))
1222 	{
1223 		/* Dragged actor is a favourite icon from quicklaunch. So hide
1224 		 * "applications" button and show an unhighlited trash button instead.
1225 		 * Trash button will be hidden and "applications" button shown again
1226 		 * when drag ends.
1227 		 */
1228 		clutter_actor_hide(priv->appsButton);
1229 		clutter_actor_show(priv->trashButton);
1230 
1231 		return(TRUE);
1232 	}
1233 
1234 	/* If we get here we cannot handle dragged actor at trash drop target */
1235 	return(FALSE);
1236 }
1237 
1238 /* Dragged actor was dropped on trash drop target */
_xfdashboard_quicklaunch_on_trash_drop_drop(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gfloat inX,gfloat inY,gpointer inUserData)1239 static void _xfdashboard_quicklaunch_on_trash_drop_drop(XfdashboardQuicklaunch *self,
1240 															XfdashboardDragAction *inDragAction,
1241 															gfloat inX,
1242 															gfloat inY,
1243 															gpointer inUserData)
1244 {
1245 	XfdashboardQuicklaunchPrivate	*priv;
1246 	ClutterActor					*draggedActor;
1247 	GAppInfo						*appInfo;
1248 
1249 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1250 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1251 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1252 
1253 	priv=self->priv;
1254 
1255 	/* Get dragged favourite icon */
1256 	draggedActor=xfdashboard_drag_action_get_actor(inDragAction);
1257 
1258 	/* Notify about removal of favourite icon */
1259 	xfdashboard_notify(CLUTTER_ACTOR(self),
1260 						xfdashboard_application_button_get_icon_name(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)),
1261 						_("Favourite '%s' removed"),
1262 						xfdashboard_application_button_get_display_name(XFDASHBOARD_APPLICATION_BUTTON(draggedActor)));
1263 
1264 	/* Emit signal and re-add removed favourite as dynamically added
1265 	 * application button for non-favourites apps when it is still running.
1266 	 */
1267 	appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(draggedActor));
1268 	if(appInfo)
1269 	{
1270 		/* Emit signal */
1271 		g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_REMOVED], 0, appInfo);
1272 
1273 		/* Re-add removed favourite as dynamically added application button
1274 		 * for non-favourites apps when it is still running.
1275 		 */
1276 		if(xfdashboard_application_tracker_is_running_by_app_info(priv->appTracker, appInfo))
1277 		{
1278 			ClutterActor			*actor;
1279 
1280 			actor=_xfdashboard_quicklaunch_create_dynamic_actor(self, appInfo);
1281 			clutter_actor_show(actor);
1282 			clutter_actor_add_child(CLUTTER_ACTOR(self), actor);
1283 		}
1284 	}
1285 
1286 	/* Destroy dragged favourite icon before updating property */
1287 	xfdashboard_actor_destroy(draggedActor);
1288 
1289 	/* Destroy preview icon before updating property */
1290 	if(priv->dragPreviewIcon)
1291 	{
1292 		xfdashboard_actor_destroy(priv->dragPreviewIcon);
1293 		priv->dragPreviewIcon=NULL;
1294 	}
1295 
1296 	/* Show "applications" button again and hide trash button instead */
1297 	clutter_actor_hide(priv->trashButton);
1298 	clutter_actor_show(priv->appsButton);
1299 
1300 	/* Update favourites from icon order */
1301 	_xfdashboard_quicklaunch_update_property_from_icons(self);
1302 
1303 	/* Reset drag mode */
1304 	priv->dragMode=DRAG_MODE_NONE;
1305 }
1306 
1307 /* Drag of an actor to trash drop target ended without actor being dropped here */
_xfdashboard_quicklaunch_on_trash_drop_end(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1308 static void _xfdashboard_quicklaunch_on_trash_drop_end(XfdashboardQuicklaunch *self,
1309 														XfdashboardDragAction *inDragAction,
1310 														gpointer inUserData)
1311 {
1312 	XfdashboardQuicklaunchPrivate	*priv;
1313 
1314 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1315 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1316 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1317 
1318 	priv=self->priv;
1319 
1320 	/* Show "applications" button again and hide trash button instead */
1321 	clutter_actor_hide(priv->trashButton);
1322 	clutter_actor_show(priv->appsButton);
1323 }
1324 
1325 /* Drag of an actor entered trash drop target */
_xfdashboard_quicklaunch_on_trash_drop_enter(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1326 static void _xfdashboard_quicklaunch_on_trash_drop_enter(XfdashboardQuicklaunch *self,
1327 															XfdashboardDragAction *inDragAction,
1328 															gpointer inUserData)
1329 {
1330 	XfdashboardQuicklaunchPrivate	*priv;
1331 
1332 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1333 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1334 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1335 
1336 	priv=self->priv;
1337 
1338 	/* Set toggle state on trash drop target as dragged actor is over it */
1339 	xfdashboard_toggle_button_set_toggle_state(XFDASHBOARD_TOGGLE_BUTTON(priv->trashButton), TRUE);
1340 }
1341 
1342 /* Drag of an actor leaves trash drop target */
_xfdashboard_quicklaunch_on_trash_drop_leave(XfdashboardQuicklaunch * self,XfdashboardDragAction * inDragAction,gpointer inUserData)1343 static void _xfdashboard_quicklaunch_on_trash_drop_leave(XfdashboardQuicklaunch *self,
1344 															XfdashboardDragAction *inDragAction,
1345 															gpointer inUserData)
1346 {
1347 	XfdashboardQuicklaunchPrivate	*priv;
1348 
1349 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1350 	g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inDragAction));
1351 	g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inUserData));
1352 
1353 	priv=self->priv;
1354 
1355 	/* Set toggle state on trash drop target as dragged actor is over it */
1356 	xfdashboard_toggle_button_set_toggle_state(XFDASHBOARD_TOGGLE_BUTTON(priv->trashButton), FALSE);
1357 }
1358 
1359 /* A tooltip for a favourite will be activated */
_xfdashboard_quicklaunch_on_tooltip_activating(ClutterAction * inAction,gpointer inUserData)1360 static void _xfdashboard_quicklaunch_on_tooltip_activating(ClutterAction *inAction, gpointer inUserData)
1361 {
1362 	XfdashboardTooltipAction		*action;
1363 	XfdashboardApplicationButton	*button;
1364 
1365 	g_return_if_fail(XFDASHBOARD_IS_TOOLTIP_ACTION(inAction));
1366 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_BUTTON(inUserData));
1367 
1368 	action=XFDASHBOARD_TOOLTIP_ACTION(inAction);
1369 	button=XFDASHBOARD_APPLICATION_BUTTON(inUserData);
1370 
1371 	/* Update tooltip text to reflect favourites current display name */
1372 	xfdashboard_tooltip_action_set_text(action, xfdashboard_application_button_get_display_name(button));
1373 }
1374 
1375 /* Create actor for a dynamically added non-favourite application */
_xfdashboard_quicklaunch_create_dynamic_actor(XfdashboardQuicklaunch * self,GAppInfo * inAppInfo)1376 static ClutterActor* _xfdashboard_quicklaunch_create_dynamic_actor(XfdashboardQuicklaunch *self,
1377 																	GAppInfo *inAppInfo)
1378 {
1379 	XfdashboardQuicklaunchPrivate	*priv;
1380 	ClutterActor					*actor;
1381 	ClutterAction					*action;
1382 
1383 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
1384 	g_return_val_if_fail(G_IS_APP_INFO(inAppInfo), NULL);
1385 
1386 	priv=self->priv;
1387 
1388 	/* Create and set up actor */
1389 	actor=xfdashboard_application_button_new_from_app_info(inAppInfo);
1390 	xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(actor), priv->normalIconSize);
1391 	xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(actor), FALSE);
1392 	xfdashboard_label_set_style(XFDASHBOARD_LABEL(actor), XFDASHBOARD_LABEL_STYLE_ICON);
1393 	xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(actor), "dynamic-app");
1394 
1395 	/* Set up and add click action */
1396 	g_signal_connect_swapped(actor, "clicked", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_clicked), self);
1397 
1398 	/* Set up and add pop-up menu click action */
1399 	action=xfdashboard_click_action_new();
1400 	g_signal_connect_swapped(action, "clicked", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_popup_menu), self);
1401 	clutter_actor_add_action(actor, action);
1402 
1403 	/* Set up and add tooltip action */
1404 	action=xfdashboard_tooltip_action_new();
1405 	g_signal_connect(action, "activating", G_CALLBACK(_xfdashboard_quicklaunch_on_tooltip_activating), actor);
1406 	clutter_actor_add_action(actor, action);
1407 
1408 	/* Return newly created actor */
1409 	return(actor);
1410 }
1411 
1412 /* Create actor for a favourite application */
_xfdashboard_quicklaunch_create_favourite_actor(XfdashboardQuicklaunch * self,GAppInfo * inAppInfo)1413 static ClutterActor* _xfdashboard_quicklaunch_create_favourite_actor(XfdashboardQuicklaunch *self,
1414 																		GAppInfo *inAppInfo)
1415 {
1416 	XfdashboardQuicklaunchPrivate	*priv;
1417 	ClutterActor					*actor;
1418 	ClutterAction					*action;
1419 
1420 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
1421 	g_return_val_if_fail(G_IS_APP_INFO(inAppInfo), NULL);
1422 
1423 	priv=self->priv;
1424 
1425 	/* Create and set up actor */
1426 	actor=xfdashboard_application_button_new_from_app_info(inAppInfo);
1427 	xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(actor), priv->normalIconSize);
1428 	xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(actor), FALSE);
1429 	xfdashboard_label_set_style(XFDASHBOARD_LABEL(actor), XFDASHBOARD_LABEL_STYLE_ICON);
1430 	xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(actor), "favourite-app");
1431 
1432 	/* Set up and add click action */
1433 	g_signal_connect_swapped(actor, "clicked", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_clicked), self);
1434 
1435 	/* Set up and add pop-up menu click action */
1436 	action=xfdashboard_click_action_new();
1437 	g_signal_connect_swapped(action, "clicked", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_popup_menu), self);
1438 	clutter_actor_add_action(actor, action);
1439 
1440 	/* Set up and add drag'n'drop action */
1441 	action=xfdashboard_drag_action_new_with_source(CLUTTER_ACTOR(self));
1442 	clutter_drag_action_set_drag_threshold(CLUTTER_DRAG_ACTION(action), -1, -1);
1443 	clutter_actor_add_action(actor, action);
1444 	g_signal_connect(action, "drag-begin", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_drag_begin), self);
1445 	g_signal_connect(action, "drag-end", G_CALLBACK(_xfdashboard_quicklaunch_on_favourite_drag_end), self);
1446 
1447 	/* Set up and add tooltip action */
1448 	action=xfdashboard_tooltip_action_new();
1449 	g_signal_connect(action, "activating", G_CALLBACK(_xfdashboard_quicklaunch_on_tooltip_activating), actor);
1450 	clutter_actor_add_action(actor, action);
1451 
1452 	/* Return newly created actor */
1453 	return(actor);
1454 }
1455 
1456 /* Update property from icons in quicklaunch */
_xfdashboard_quicklaunch_update_property_from_icons(XfdashboardQuicklaunch * self)1457 static void _xfdashboard_quicklaunch_update_property_from_icons(XfdashboardQuicklaunch *self)
1458 {
1459 	XfdashboardQuicklaunchPrivate	*priv;
1460 	ClutterActor					*child;
1461 	ClutterActorIter				iter;
1462 	GAppInfo						*desktopAppInfo;
1463 	gchar							*desktopFile;
1464 	GValue							*desktopValue;
1465 
1466 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1467 
1468 	priv=self->priv;
1469 
1470 	/* Free current list of desktop files */
1471 	if(priv->favourites)
1472 	{
1473 		xfconf_array_free(priv->favourites);
1474 		priv->favourites=NULL;
1475 	}
1476 
1477 	/* Create array of strings pointing to desktop files for new order,
1478 	 * update quicklaunch and store settings
1479 	 */
1480 	priv->favourites=g_ptr_array_new();
1481 
1482 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1483 	while(clutter_actor_iter_next(&iter, &child))
1484 	{
1485 		desktopFile=NULL;
1486 
1487 		/* Only add desktop file if it is an application button for
1488 		 * a favourite and provides a desktop ID or desktop file name
1489 		 * and is not going to be destroyed
1490 		 */
1491  		if(!XFDASHBOARD_IS_APPLICATION_BUTTON(child)) continue;
1492 		if(xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "destroying")) continue;
1493 		if(!xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "favourite-app")) continue;
1494 
1495 		desktopAppInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(child));
1496 		if(desktopAppInfo &&
1497 			XFDASHBOARD_IS_DESKTOP_APP_INFO(desktopAppInfo))
1498 		{
1499 			desktopFile=g_strdup(g_app_info_get_id(desktopAppInfo));
1500 			if(!desktopFile)
1501 			{
1502 				GFile				*file;
1503 
1504 				file=xfdashboard_desktop_app_info_get_file(XFDASHBOARD_DESKTOP_APP_INFO(desktopAppInfo));
1505 				if(file) desktopFile=g_file_get_path(file);
1506 			}
1507 		}
1508 		if(!desktopFile) continue;
1509 
1510 		/* Add desktop file name to array */
1511 		desktopValue=g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
1512 		g_value_set_string(desktopValue, desktopFile);
1513 		g_ptr_array_add(priv->favourites, desktopValue);
1514 
1515 		/* Release allocated resources */
1516 		g_free(desktopFile);
1517 	}
1518 
1519 	/* Notify about property change */
1520 	g_object_notify_by_pspec(G_OBJECT(self), XfdashboardQuicklaunchProperties[PROP_FAVOURITES]);
1521 }
1522 
1523 /* Update icons in quicklaunch from property */
_xfdashboard_quicklaunch_update_icons_from_property(XfdashboardQuicklaunch * self)1524 static void _xfdashboard_quicklaunch_update_icons_from_property(XfdashboardQuicklaunch *self)
1525 {
1526 	XfdashboardQuicklaunchPrivate	*priv;
1527 	ClutterActor					*child;
1528 	ClutterActorIter				iter;
1529 	guint							i;
1530 	ClutterActor					*actor;
1531 	GValue							*desktopFile;
1532 	GAppInfo						*currentSelectionAppInfo;
1533 	const gchar						*desktopFilename;
1534 	GAppInfo						*appInfo;
1535 
1536 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1537 
1538 	priv=self->priv;
1539 	currentSelectionAppInfo=NULL;
1540 
1541 	/* If current selection is an application button then remember it
1542 	 * to reselect it after favourites are re-setup.
1543 	 */
1544 	if(priv->selectedItem &&
1545 		XFDASHBOARD_IS_APPLICATION_BUTTON(priv->selectedItem))
1546 	{
1547 		currentSelectionAppInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(priv->selectedItem));
1548 
1549 		XFDASHBOARD_DEBUG(self, ACTOR,
1550 							"Going to destroy current selection %p (%s) for desktop ID '%s'",
1551 							priv->selectedItem, G_OBJECT_TYPE_NAME(priv->selectedItem),
1552 							g_app_info_get_id(xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(priv->selectedItem))));
1553 	}
1554 
1555 	/* Remove all application buttons */
1556 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1557 	while(clutter_actor_iter_next(&iter, &child))
1558 	{
1559 		if(XFDASHBOARD_IS_APPLICATION_BUTTON(child) &&
1560 			xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(child), "favourite-app"))
1561 		{
1562 			clutter_actor_iter_destroy(&iter);
1563 		}
1564 	}
1565 
1566 	/* Now re-add all application icons for current favourites */
1567 	for(i=0; i<priv->favourites->len; i++)
1568 	{
1569 		/* Get desktop file to create application button for in quicklaunch */
1570 		desktopFile=(GValue*)g_ptr_array_index(priv->favourites, i);
1571 
1572 		desktopFilename=g_value_get_string(desktopFile);
1573 		if(g_path_is_absolute(desktopFilename)) appInfo=xfdashboard_desktop_app_info_new_from_path(desktopFilename);
1574 			else
1575 			{
1576 				appInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDB, desktopFilename);
1577 				if(!appInfo) appInfo=xfdashboard_desktop_app_info_new_from_desktop_id(desktopFilename);
1578 			}
1579 
1580 		/* If we could not get application information for desktop file, do not
1581 		 * create the application button.
1582 		 */
1583 		if(!appInfo) continue;
1584 
1585 		/* Create application button from desktop file */
1586 		actor=_xfdashboard_quicklaunch_create_favourite_actor(self, appInfo);
1587 		clutter_actor_show(actor);
1588 		clutter_actor_insert_child_below(CLUTTER_ACTOR(self), actor, priv->separatorFavouritesToDynamic);
1589 
1590 		/* Select this item if it matches the previously selected item
1591 		 * which was destroyed in the meantime.
1592 		 */
1593 		if(currentSelectionAppInfo)
1594 		{
1595 			/* Check if newly created application button matches current selection
1596 			 * then reselect newly create actor as current selection.
1597 			 */
1598 			if(g_app_info_equal(G_APP_INFO(appInfo), currentSelectionAppInfo))
1599 			{
1600 				xfdashboard_focusable_set_selection(XFDASHBOARD_FOCUSABLE(self), actor);
1601 
1602 				XFDASHBOARD_DEBUG(self, ACTOR,
1603 									"Select newly created actor %p (%s) because it matches desktop ID '%s'",
1604 									actor, G_OBJECT_TYPE_NAME(actor),
1605 									g_app_info_get_id(xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(actor))));
1606 			}
1607 		}
1608 
1609 		/* Release allocated resources */
1610 		g_object_unref(appInfo);
1611 	}
1612 }
1613 
1614 /* Set up favourites array from string array value */
_xfdashboard_quicklaunch_set_favourites(XfdashboardQuicklaunch * self,const GValue * inValue)1615 static void _xfdashboard_quicklaunch_set_favourites(XfdashboardQuicklaunch *self, const GValue *inValue)
1616 {
1617 	XfdashboardQuicklaunchPrivate	*priv;
1618 	GPtrArray						*desktopFiles;
1619 	guint							i;
1620 	GValue							*element;
1621 	GValue							*desktopFile;
1622 
1623 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1624 	g_return_if_fail(G_IS_VALUE(inValue));
1625 
1626 	priv=self->priv;
1627 
1628 	/* Free current list of favourites */
1629 	if(priv->favourites)
1630 	{
1631 		xfconf_array_free(priv->favourites);
1632 		priv->favourites=NULL;
1633 	}
1634 
1635 	/* Copy array of string pointing to desktop files */
1636 	desktopFiles=g_value_get_boxed(inValue);
1637 	if(desktopFiles)
1638 	{
1639 		priv->favourites=g_ptr_array_sized_new(desktopFiles->len);
1640 		for(i=0; i<desktopFiles->len; ++i)
1641 		{
1642 			element=(GValue*)g_ptr_array_index(desktopFiles, i);
1643 
1644 			/* Filter string value types */
1645 			if(G_VALUE_HOLDS_STRING(element))
1646 			{
1647 				desktopFile=g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
1648 				g_value_copy(element, desktopFile);
1649 				g_ptr_array_add(priv->favourites, desktopFile);
1650 			}
1651 		}
1652 	}
1653 		else priv->favourites=g_ptr_array_new();
1654 
1655 	/* Update list of icons for desktop files */
1656 	_xfdashboard_quicklaunch_update_icons_from_property(self);
1657 }
1658 
1659 /* Set up default favourites (e.g. used when started for the very first time) */
_xfdashboard_quicklaunch_setup_default_favourites(XfdashboardQuicklaunch * self)1660 static void _xfdashboard_quicklaunch_setup_default_favourites(XfdashboardQuicklaunch *self)
1661 {
1662 	XfdashboardQuicklaunchPrivate	*priv;
1663 	guint							i;
1664 	const gchar						*defaultApplications[]=	{
1665 																"exo-web-browser.desktop",
1666 																"exo-mail-reader.desktop",
1667 																"exo-file-manager.desktop",
1668 																"exo-terminal-emulator.desktop",
1669 															};
1670 
1671 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
1672 
1673 	priv=self->priv;
1674 
1675 	/* Free current list of favourites */
1676 	if(priv->favourites)
1677 	{
1678 		xfconf_array_free(priv->favourites);
1679 		priv->favourites=NULL;
1680 	}
1681 
1682 	/* Build array with each available default application */
1683 	priv->favourites=g_ptr_array_new();
1684 	for(i=0; i<(sizeof(defaultApplications)/sizeof(defaultApplications[0])); i++)
1685 	{
1686 		GAppInfo					*appInfo;
1687 		GValue						*desktopFile;
1688 
1689 		appInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDB, defaultApplications[i]);
1690 		if(!appInfo) appInfo=xfdashboard_desktop_app_info_new_from_desktop_id(defaultApplications[i]);
1691 
1692 		if(appInfo)
1693 		{
1694 			/* Add desktop file to array */
1695 			desktopFile=g_value_init(g_new0(GValue, 1), G_TYPE_STRING);
1696 			g_value_set_string(desktopFile, defaultApplications[i]);
1697 			g_ptr_array_add(priv->favourites, desktopFile);
1698 
1699 			/* Release allocated resources */
1700 			g_object_unref(appInfo);
1701 		}
1702 	}
1703 
1704 	/* Notify about property change */
1705 	g_object_notify_by_pspec(G_OBJECT(self), XfdashboardQuicklaunchProperties[PROP_FAVOURITES]);
1706 }
1707 
1708 /* Get scale factor to fit all children into given width */
_xfdashboard_quicklaunch_get_scale_for_width(XfdashboardQuicklaunch * self,gfloat inForWidth,gboolean inDoMinimumSize)1709 static gfloat _xfdashboard_quicklaunch_get_scale_for_width(XfdashboardQuicklaunch *self,
1710 															gfloat inForWidth,
1711 															gboolean inDoMinimumSize)
1712 {
1713 	XfdashboardQuicklaunchPrivate	*priv;
1714 	ClutterActor					*child;
1715 	ClutterActorIter				iter;
1716 	gint							numberChildren;
1717 	gfloat							totalWidth, scalableWidth;
1718 	gfloat							childWidth;
1719 	gfloat							childMinWidth, childNaturalWidth;
1720 	gfloat							scale;
1721 	gboolean						recheckWidth;
1722 
1723 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), 0.0f);
1724 	g_return_val_if_fail(inForWidth>=0.0f, 0.0f);
1725 
1726 	priv=self->priv;
1727 
1728 	/* Count visible children and determine their total width */
1729 	numberChildren=0;
1730 	totalWidth=0.0f;
1731 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1732 	while(clutter_actor_iter_next(&iter, &child))
1733 	{
1734 		/* Only check visible children */
1735 		if(!clutter_actor_is_visible(child)) continue;
1736 
1737 		/* Get width of child */
1738 		clutter_actor_get_preferred_width(child, -1, &childMinWidth, &childNaturalWidth);
1739 		if(inDoMinimumSize==TRUE) childWidth=childMinWidth;
1740 			else childWidth=childNaturalWidth;
1741 
1742 		/* Determine total size so far */
1743 		totalWidth+=ceil(childWidth);
1744 
1745 		/* Count visible children */
1746 		numberChildren++;
1747 	}
1748 	if(numberChildren==0) return(priv->scaleMax);
1749 
1750 	/* Determine scalable width. That is the width without spacing
1751 	 * between children and the spacing used as padding.
1752 	 */
1753 	scalableWidth=inForWidth-((numberChildren+1)*priv->spacing);
1754 
1755 	/* Get scale factor */
1756 	scale=priv->scaleMax;
1757 	if(totalWidth>0.0f)
1758 	{
1759 		scale=floor((scalableWidth/totalWidth)/priv->scaleStep)*priv->scaleStep;
1760 		scale=MIN(scale, priv->scaleMax);
1761 		scale=MAX(scale, priv->scaleMin);
1762 	}
1763 
1764 	/* Check if all visible children would really fit into width
1765 	 * otherwise we need to decrease scale factor one step down
1766 	 */
1767 	if(scale>priv->scaleMin)
1768 	{
1769 		do
1770 		{
1771 			recheckWidth=FALSE;
1772 			totalWidth=priv->spacing;
1773 
1774 			/* Iterate through visible children and sum their scaled
1775 			 * widths. The total width will be initialized with unscaled
1776 			 * spacing and all visible children's scaled width will also
1777 			 * be added with unscaled spacing to have the padding added.
1778 			 */
1779 			clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1780 			while(clutter_actor_iter_next(&iter, &child))
1781 			{
1782 				/* Only check visible children */
1783 				if(!clutter_actor_is_visible(child)) continue;
1784 
1785 				/* Get scaled size of child and add to total width */
1786 				clutter_actor_get_preferred_width(child, -1, &childMinWidth, &childNaturalWidth);
1787 				if(inDoMinimumSize==TRUE) childWidth=childMinWidth;
1788 					else childWidth=childNaturalWidth;
1789 
1790 				childWidth*=scale;
1791 				totalWidth+=ceil(childWidth)+priv->spacing;
1792 			}
1793 
1794 			/* If total width is greater than given width
1795 			 * decrease scale factor by one step and recheck
1796 			 */
1797 			if(totalWidth>inForWidth && scale>priv->scaleMin)
1798 			{
1799 				scale-=priv->scaleStep;
1800 				recheckWidth=TRUE;
1801 			}
1802 		}
1803 		while(recheckWidth==TRUE);
1804 	}
1805 
1806 	/* Return found scale factor */
1807 	return(scale);
1808 }
1809 
1810 /* Get scale factor to fit all children into given height */
_xfdashboard_quicklaunch_get_scale_for_height(XfdashboardQuicklaunch * self,gfloat inForHeight,gboolean inDoMinimumSize)1811 static gfloat _xfdashboard_quicklaunch_get_scale_for_height(XfdashboardQuicklaunch *self,
1812 															gfloat inForHeight,
1813 															gboolean inDoMinimumSize)
1814 {
1815 	XfdashboardQuicklaunchPrivate	*priv;
1816 	ClutterActor					*child;
1817 	ClutterActorIter				iter;
1818 	gint							numberChildren;
1819 	gfloat							totalHeight, scalableHeight;
1820 	gfloat							childHeight;
1821 	gfloat							childMinHeight, childNaturalHeight;
1822 	gfloat							scale;
1823 	gboolean						recheckHeight;
1824 
1825 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), 0.0f);
1826 	g_return_val_if_fail(inForHeight>=0.0f, 0.0f);
1827 
1828 	priv=self->priv;
1829 
1830 	/* Count visible children and determine their total height */
1831 	numberChildren=0;
1832 	totalHeight=0.0f;
1833 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1834 	while(clutter_actor_iter_next(&iter, &child))
1835 	{
1836 		/* Only check visible children */
1837 		if(!clutter_actor_is_visible(child)) continue;
1838 
1839 		/* Get height of child */
1840 		clutter_actor_get_preferred_height(child, -1, &childMinHeight, &childNaturalHeight);
1841 		if(inDoMinimumSize==TRUE) childHeight=childMinHeight;
1842 			else childHeight=childNaturalHeight;
1843 
1844 		/* Determine total size so far */
1845 		totalHeight+=ceil(childHeight);
1846 
1847 		/* Count visible children */
1848 		numberChildren++;
1849 	}
1850 	if(numberChildren==0) return(priv->scaleMax);
1851 
1852 	/* Determine scalable height. That is the height without spacing
1853 	 * between children and the spacing used as padding.
1854 	 */
1855 	scalableHeight=inForHeight-((numberChildren+1)*priv->spacing);
1856 
1857 	/* Get scale factor */
1858 	scale=priv->scaleMax;
1859 	if(totalHeight>0.0f)
1860 	{
1861 		scale=floor((scalableHeight/totalHeight)/priv->scaleStep)*priv->scaleStep;
1862 		scale=MIN(scale, priv->scaleMax);
1863 		scale=MAX(scale, priv->scaleMin);
1864 	}
1865 
1866 	/* Check if all visible children would really fit into height
1867 	 * otherwise we need to decrease scale factor one step down
1868 	 */
1869 	if(scale>priv->scaleMin)
1870 	{
1871 		do
1872 		{
1873 			recheckHeight=FALSE;
1874 			totalHeight=priv->spacing;
1875 
1876 			/* Iterate through visible children and sum their scaled
1877 			 * heights. The total height will be initialized with unscaled
1878 			 * spacing and all visible children's scaled height will also
1879 			 * be added with unscaled spacing to have the padding added.
1880 			 */
1881 			clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1882 			while(clutter_actor_iter_next(&iter, &child))
1883 			{
1884 				/* Only check visible children */
1885 				if(!clutter_actor_is_visible(child)) continue;
1886 
1887 				/* Get scaled size of child and add to total height */
1888 				clutter_actor_get_preferred_height(child, -1, &childMinHeight, &childNaturalHeight);
1889 				if(inDoMinimumSize==TRUE) childHeight=childMinHeight;
1890 					else childHeight=childNaturalHeight;
1891 
1892 				childHeight*=scale;
1893 				totalHeight+=ceil(childHeight)+priv->spacing;
1894 			}
1895 
1896 			/* If total height is greater than given height
1897 			 * decrease scale factor by one step and recheck
1898 			 */
1899 			if(totalHeight>inForHeight && scale>priv->scaleMin)
1900 			{
1901 				scale-=priv->scaleStep;
1902 				recheckHeight=TRUE;
1903 			}
1904 		}
1905 		while(recheckHeight==TRUE);
1906 	}
1907 
1908 	/* Return found scale factor */
1909 	return(scale);
1910 }
1911 
1912 /* Get previous selectable actor in quicklaunch */
xfdashboard_quicklaunch_get_previous_selectable(XfdashboardQuicklaunch * self,ClutterActor * inSelected)1913 static ClutterActor* xfdashboard_quicklaunch_get_previous_selectable(XfdashboardQuicklaunch *self,
1914 																		ClutterActor *inSelected)
1915 {
1916 	ClutterActorIter	iter;
1917 	ClutterActor		*child;
1918 	ClutterActor		*prevItem;
1919 
1920 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
1921 
1922 	prevItem=NULL;
1923 
1924 	/* Iterate through children and return previous selectable item after given one */
1925 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1926 	while(clutter_actor_iter_next(&iter, &child))
1927 	{
1928 		/* If this child is the lookup one return previous visible item seen */
1929 		if(child==inSelected && prevItem) return(prevItem);
1930 
1931 		/* If this child is visible but not the one we lookup remember it as previous one */
1932 		if(clutter_actor_is_visible(child)) prevItem=child;
1933 	}
1934 
1935 	/* If we get here there is no selectable item after given one, so return last
1936 	 * selectable item we have seen.
1937 	 */
1938 	return(prevItem);
1939 }
1940 
1941 /* Get next selectable actor in quicklaunch */
xfdashboard_quicklaunch_get_next_selectable(XfdashboardQuicklaunch * self,ClutterActor * inSelected)1942 static ClutterActor* xfdashboard_quicklaunch_get_next_selectable(XfdashboardQuicklaunch *self,
1943 																	ClutterActor *inSelected)
1944 {
1945 	ClutterActorIter	iter;
1946 	ClutterActor		*child;
1947 	gboolean			doLookup;
1948 
1949 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
1950 
1951 	/* Iterate through children beginnig at current selected one and return next
1952 	 * selectable item after that one.
1953 	 */
1954 	doLookup=FALSE;
1955 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1956 	while(clutter_actor_iter_next(&iter, &child))
1957 	{
1958 		/* If this child is the lookup one, lookup next visible item */
1959 		if(child!=inSelected && doLookup==FALSE) continue;
1960 
1961 		/* Return child if visible */
1962 		if(doLookup && clutter_actor_is_visible(child)) return(child);
1963 
1964 		/* If we get here we either found current selected one and we should
1965 		 * look for next selectable item or we looked for next selectable item
1966 		 * but it was not selectable. In both cases set flag to lookup for
1967 		 * selectable items.
1968 		 */
1969 		doLookup=TRUE;
1970 	}
1971 
1972 	/* If we get here we are at the end of list of children and no one was selectable.
1973 	 * Start over at the beginning of list of children up to the current selected one.
1974 	 * Return the first visible item.
1975 	 */
1976 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(self));
1977 	while(clutter_actor_iter_next(&iter, &child))
1978 	{
1979 		/* Stop at current selected one */
1980 		if(child==inSelected) break;
1981 
1982 		/* Return this child if visible */
1983 		if(clutter_actor_is_visible(child)) return(child);
1984 	}
1985 
1986 	/* If we get here there is no selectable item was found, so return NULL */
1987 	return(NULL);
1988 }
1989 
1990 /* Action signal to add current selected item as favourite was emitted */
_xfdashboard_quicklaunch_selection_add_favourite(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)1991 static gboolean _xfdashboard_quicklaunch_selection_add_favourite(XfdashboardQuicklaunch *self,
1992 																	XfdashboardFocusable *inSource,
1993 																	const gchar *inAction,
1994 																	ClutterEvent *inEvent)
1995 {
1996 	XfdashboardQuicklaunchPrivate	*priv;
1997 	ClutterActor					*currentSelection;
1998 	GAppInfo						*appInfo;
1999 
2000 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), CLUTTER_EVENT_PROPAGATE);
2001 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inSource), CLUTTER_EVENT_PROPAGATE);
2002 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
2003 
2004 	priv=self->priv;
2005 
2006 	/* Get current selection of focusable actor and check if it is an actor
2007 	 * derived from XfdashboardApplicationButton.
2008 	 */
2009 	currentSelection=xfdashboard_focusable_get_selection(inSource);
2010 	if(!currentSelection)
2011 	{
2012 		XFDASHBOARD_DEBUG(self, ACTOR,
2013 							"Source actor %s has no selection and no favourite can be added.",
2014 							G_OBJECT_TYPE_NAME(inSource));
2015 		return(CLUTTER_EVENT_STOP);
2016 	}
2017 
2018 	if(!XFDASHBOARD_IS_APPLICATION_BUTTON(currentSelection))
2019 	{
2020 		XFDASHBOARD_DEBUG(self, ACTOR,
2021 							"Current selection at source actor %s has type %s but only selections of type %s can be added.",
2022 							G_OBJECT_TYPE_NAME(inSource),
2023 							G_OBJECT_TYPE_NAME(currentSelection),
2024 							g_type_name(XFDASHBOARD_TYPE_APPLICATION_BUTTON));
2025 		return(CLUTTER_EVENT_STOP);
2026 	}
2027 
2028 	/* Check if application button provides all information needed to add favourite
2029 	 * and also check for duplicates.
2030 	 */
2031 	appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(currentSelection));
2032 	if(appInfo &&
2033 		!_xfdashboard_quicklaunch_has_favourite_appinfo(self, appInfo))
2034 	{
2035 		ClutterActor		*favouriteActor;
2036 
2037 		/* If an actor for current selection to add to favourites already exists,
2038 		 * destroy and remove it regardless if it an actor or a favourite app or
2039 		 * dynamic non-favourite app. It will be re-added later.
2040 		 */
2041 		favouriteActor=_xfdashboard_quicklaunch_get_actor_for_appinfo(self, appInfo);
2042 		if(favouriteActor)
2043 		{
2044 			/* Remove existing actor from this quicklaunch */
2045 			xfdashboard_actor_destroy(favouriteActor);
2046 		}
2047 
2048 		/* Now (re-)add current selection to favourites but hidden as
2049 		 * it will become visible and properly set up when function
2050 		 * _xfdashboard_quicklaunch_update_property_from_icons is called.
2051 		 */
2052 		favouriteActor=xfdashboard_application_button_new_from_app_info(appInfo);
2053 		clutter_actor_hide(favouriteActor);
2054 		xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(favouriteActor), "favourite-app");
2055 		clutter_actor_insert_child_below(CLUTTER_ACTOR(self), favouriteActor, priv->separatorFavouritesToDynamic);
2056 
2057 		/* Update favourites from icon order */
2058 		_xfdashboard_quicklaunch_update_property_from_icons(self);
2059 
2060 		/* Notify about new favourite */
2061 		xfdashboard_notify(CLUTTER_ACTOR(self),
2062 							xfdashboard_application_button_get_icon_name(XFDASHBOARD_APPLICATION_BUTTON(favouriteActor)),
2063 							_("Favourite '%s' added"),
2064 							xfdashboard_application_button_get_display_name(XFDASHBOARD_APPLICATION_BUTTON(favouriteActor)));
2065 
2066 		g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_ADDED], 0, appInfo);
2067 	}
2068 
2069 	return(CLUTTER_EVENT_STOP);
2070 }
2071 
2072 /* Action signal to remove current selected item as favourite was emitted */
_xfdashboard_quicklaunch_selection_remove_favourite(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)2073 static gboolean _xfdashboard_quicklaunch_selection_remove_favourite(XfdashboardQuicklaunch *self,
2074 																	XfdashboardFocusable *inSource,
2075 																	const gchar *inAction,
2076 																	ClutterEvent *inEvent)
2077 {
2078 	XfdashboardQuicklaunchPrivate	*priv;
2079 	ClutterActor					*currentSelection;
2080 	ClutterActor					*newSelection;
2081 	GAppInfo						*appInfo;
2082 
2083 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), CLUTTER_EVENT_PROPAGATE);
2084 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inSource), CLUTTER_EVENT_PROPAGATE);
2085 	g_return_val_if_fail(inEvent, CLUTTER_EVENT_PROPAGATE);
2086 
2087 	priv=self->priv;
2088 
2089 	/* If this binding action was not emitted on this quicklaunch
2090 	 * then propagate event because there might be another quicklaunch
2091 	 * which will handle it.
2092 	 */
2093 	if(XFDASHBOARD_QUICKLAUNCH(inSource)!=self) return(CLUTTER_EVENT_PROPAGATE);
2094 
2095 	/* Check if current selection in this quicklaunch is a favourite */
2096 	currentSelection=xfdashboard_focusable_get_selection(inSource);
2097 	if(!currentSelection)
2098 	{
2099 		XFDASHBOARD_DEBUG(self, ACTOR,
2100 							"Source actor %s has no selection so no favourite can be removed.",
2101 							G_OBJECT_TYPE_NAME(inSource));
2102 		return(CLUTTER_EVENT_STOP);
2103 	}
2104 
2105 	if(!XFDASHBOARD_IS_APPLICATION_BUTTON(currentSelection))
2106 	{
2107 		XFDASHBOARD_DEBUG(self, ACTOR,
2108 							"Current selection at source actor %s has type %s but only selections of type %s can be removed.",
2109 							G_OBJECT_TYPE_NAME(inSource),
2110 							G_OBJECT_TYPE_NAME(currentSelection),
2111 							g_type_name(XFDASHBOARD_TYPE_APPLICATION_BUTTON));
2112 		return(CLUTTER_EVENT_STOP);
2113 	}
2114 
2115 	if(priv->dragPreviewIcon && currentSelection==priv->dragPreviewIcon)
2116 	{
2117 		XFDASHBOARD_DEBUG(self, ACTOR,
2118 							"Current selection at source actor %s is %s which is the drag preview icon which cannot be removed.",
2119 							G_OBJECT_TYPE_NAME(inSource),
2120 							G_OBJECT_TYPE_NAME(currentSelection));
2121 		return(CLUTTER_EVENT_STOP);
2122 	}
2123 
2124 	/* Get application information */
2125 	appInfo=xfdashboard_application_button_get_app_info(XFDASHBOARD_APPLICATION_BUTTON(currentSelection));
2126 
2127 	/* Notify about removed favourite */
2128 	xfdashboard_notify(CLUTTER_ACTOR(self),
2129 						xfdashboard_application_button_get_icon_name(XFDASHBOARD_APPLICATION_BUTTON(currentSelection)),
2130 						_("Favourite '%s' removed"),
2131 						xfdashboard_application_button_get_display_name(XFDASHBOARD_APPLICATION_BUTTON(currentSelection)));
2132 
2133 	if(appInfo) g_signal_emit(self, XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_REMOVED], 0, appInfo);
2134 
2135 	/* Select previous or next actor in quicklaunch if the favourite
2136 	 * going to be removed is the currently selected one.
2137 	 */
2138 	newSelection=clutter_actor_get_next_sibling(currentSelection);
2139 	if(!newSelection) newSelection=clutter_actor_get_previous_sibling(currentSelection);
2140 	if(!newSelection) newSelection=clutter_actor_get_last_child(CLUTTER_ACTOR(self));
2141 
2142 	if(newSelection)
2143 	{
2144 		xfdashboard_focusable_set_selection(XFDASHBOARD_FOCUSABLE(self), newSelection);
2145 	}
2146 
2147 	/* Remove actor from this quicklaunch */
2148 	xfdashboard_actor_destroy(currentSelection);
2149 
2150 	/* Re-add removed favourite as dynamically added application button
2151 	 * for non-favourites apps when it is still running.
2152 	 */
2153 	if(appInfo &&
2154 		xfdashboard_application_tracker_is_running_by_app_info(priv->appTracker, appInfo))
2155 	{
2156 		ClutterActor			*actor;
2157 
2158 		actor=_xfdashboard_quicklaunch_create_dynamic_actor(self, appInfo);
2159 		clutter_actor_show(actor);
2160 		clutter_actor_add_child(CLUTTER_ACTOR(self), actor);
2161 	}
2162 
2163 	/* Update favourites from icon order */
2164 	_xfdashboard_quicklaunch_update_property_from_icons(self);
2165 
2166 	/* Action handled */
2167 	return(CLUTTER_EVENT_STOP);
2168 }
2169 
2170 /* Action signal to move current selected item to reorder items was emitted */
_xfdashboard_quicklaunch_favourite_reorder_selection(XfdashboardQuicklaunch * self,XfdashboardOrientation inDirection)2171 static gboolean _xfdashboard_quicklaunch_favourite_reorder_selection(XfdashboardQuicklaunch *self,
2172 																		XfdashboardOrientation inDirection)
2173 {
2174 	XfdashboardQuicklaunchPrivate	*priv;
2175 	ClutterActor					*currentSelection;
2176 	ClutterActor					*newSelection;
2177 	ClutterOrientation				orientation;
2178 
2179 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), CLUTTER_EVENT_PROPAGATE);
2180 	g_return_val_if_fail(inDirection<=XFDASHBOARD_ORIENTATION_BOTTOM, CLUTTER_EVENT_PROPAGATE);
2181 
2182 	priv=self->priv;
2183 
2184 	/* Determine expected orientation of quicklaunch */
2185 	if(inDirection==XFDASHBOARD_ORIENTATION_LEFT ||
2186 		inDirection==XFDASHBOARD_ORIENTATION_RIGHT)
2187 	{
2188 		orientation=CLUTTER_ORIENTATION_HORIZONTAL;
2189 	}
2190 		else orientation=CLUTTER_ORIENTATION_VERTICAL;
2191 
2192 	/* Orientation of quicklaunch must match orientation of direction
2193 	 * to which we should reorder item.
2194 	 */
2195 	if(priv->orientation!=orientation)
2196 	{
2197 		XFDASHBOARD_DEBUG(self, ACTOR,
2198 							"Source actor %s does not have expected orientation.",
2199 							G_OBJECT_TYPE_NAME(self));
2200 		return(CLUTTER_EVENT_STOP);
2201 	}
2202 
2203 	/* Check if current selection in this quicklaunch is a favourite */
2204 	currentSelection=xfdashboard_focusable_get_selection(XFDASHBOARD_FOCUSABLE(self));
2205 	if(!currentSelection)
2206 	{
2207 		XFDASHBOARD_DEBUG(self, ACTOR,
2208 							"Source actor %s has no selection so no favourite can be reordered.",
2209 							G_OBJECT_TYPE_NAME(self));
2210 		return(CLUTTER_EVENT_STOP);
2211 	}
2212 
2213 	if(!XFDASHBOARD_IS_APPLICATION_BUTTON(currentSelection))
2214 	{
2215 		XFDASHBOARD_DEBUG(self, ACTOR,
2216 							"Current selection at source actor %s has type %s but only selections of type %s can be reordered.",
2217 							G_OBJECT_TYPE_NAME(self),
2218 							G_OBJECT_TYPE_NAME(currentSelection),
2219 							g_type_name(XFDASHBOARD_TYPE_APPLICATION_BUTTON));
2220 		return(CLUTTER_EVENT_STOP);
2221 	}
2222 
2223 	if(!xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(currentSelection), "favourite-app"))
2224 	{
2225 		XFDASHBOARD_DEBUG(self, ACTOR,
2226 							"Current selection at source actor %s is not a favourite and cannot be reordered.",
2227 							G_OBJECT_TYPE_NAME(self));
2228 		return(CLUTTER_EVENT_STOP);
2229 	}
2230 
2231 	if(priv->dragPreviewIcon && currentSelection==priv->dragPreviewIcon)
2232 	{
2233 		XFDASHBOARD_DEBUG(self, ACTOR,
2234 							"Current selection at source actor %s is %s which is the drag preview icon which cannot be reordered.",
2235 							G_OBJECT_TYPE_NAME(self),
2236 							G_OBJECT_TYPE_NAME(currentSelection));
2237 		return(CLUTTER_EVENT_STOP);
2238 	}
2239 
2240 	/* Find new position and check if current selection can be moved to this new position.
2241 	 * The current selection cannot be moved if it is already at the beginning or the end
2242 	 * and it cannot bypass an actor of type XfdashboardButton which is used by any
2243 	 * non favourite actor like "switch" button, trash button etc. or ClutterActor used
2244 	 * by separators. Favourites or dynamic non-favourites use actors of type
2245 	 * XfdashboardApplicationButton. So each non-favourite button cannot get into favourites
2246 	 * area and vice versa.
2247 	 */
2248 	if(inDirection==XFDASHBOARD_ORIENTATION_LEFT ||
2249 		inDirection==XFDASHBOARD_ORIENTATION_TOP)
2250 	{
2251 		newSelection=clutter_actor_get_previous_sibling(currentSelection);
2252 	}
2253 		else newSelection=clutter_actor_get_next_sibling(currentSelection);
2254 
2255 	if(!newSelection)
2256 	{
2257 		XFDASHBOARD_DEBUG(self, ACTOR,
2258 							"Current selection %s at source actor %s is already at end of list",
2259 							G_OBJECT_TYPE_NAME(currentSelection),
2260 							G_OBJECT_TYPE_NAME(self));
2261 		return(CLUTTER_EVENT_STOP);
2262 	}
2263 
2264 	if(!XFDASHBOARD_IS_APPLICATION_BUTTON(newSelection))
2265 	{
2266 		XFDASHBOARD_DEBUG(self, ACTOR,
2267 							"Current selection %s at source actor %s cannot be moved because it is blocked by %s.",
2268 							G_OBJECT_TYPE_NAME(currentSelection),
2269 							G_OBJECT_TYPE_NAME(self),
2270 							G_OBJECT_TYPE_NAME(newSelection));
2271 		return(CLUTTER_EVENT_STOP);
2272 	}
2273 
2274 	/* Move current selection to new position */
2275 	if(inDirection==XFDASHBOARD_ORIENTATION_LEFT ||
2276 		inDirection==XFDASHBOARD_ORIENTATION_TOP)
2277 	{
2278 		clutter_actor_set_child_below_sibling(CLUTTER_ACTOR(self),
2279 												currentSelection,
2280 												newSelection);
2281 	}
2282 		else
2283 		{
2284 			clutter_actor_set_child_above_sibling(CLUTTER_ACTOR(self),
2285 													currentSelection,
2286 													newSelection);
2287 		}
2288 
2289 	/* Update favourites from icon order */
2290 	_xfdashboard_quicklaunch_update_property_from_icons(self);
2291 
2292 	/* Action handled */
2293 	return(CLUTTER_EVENT_STOP);
2294 }
2295 
_xfdashboard_quicklaunch_favourite_reorder_left(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)2296 static gboolean _xfdashboard_quicklaunch_favourite_reorder_left(XfdashboardQuicklaunch *self,
2297 																XfdashboardFocusable *inSource,
2298 																const gchar *inAction,
2299 																ClutterEvent *inEvent)
2300 {
2301 	return(_xfdashboard_quicklaunch_favourite_reorder_selection(self, XFDASHBOARD_ORIENTATION_LEFT));
2302 }
2303 
_xfdashboard_quicklaunch_favourite_reorder_right(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)2304 static gboolean _xfdashboard_quicklaunch_favourite_reorder_right(XfdashboardQuicklaunch *self,
2305 																	XfdashboardFocusable *inSource,
2306 																	const gchar *inAction,
2307 																	ClutterEvent *inEvent)
2308 {
2309 	return(_xfdashboard_quicklaunch_favourite_reorder_selection(self, XFDASHBOARD_ORIENTATION_RIGHT));
2310 }
2311 
2312 /* Action signal to move current selected item to up (to reorder items) was emitted */
_xfdashboard_quicklaunch_favourite_reorder_up(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)2313 static gboolean _xfdashboard_quicklaunch_favourite_reorder_up(XfdashboardQuicklaunch *self,
2314 																XfdashboardFocusable *inSource,
2315 																const gchar *inAction,
2316 																ClutterEvent *inEvent)
2317 {
2318 	return(_xfdashboard_quicklaunch_favourite_reorder_selection(self, XFDASHBOARD_ORIENTATION_TOP));
2319 }
2320 
2321 /* Action signal to move current selected item to down (to reorder items) was emitted */
_xfdashboard_quicklaunch_favourite_reorder_down(XfdashboardQuicklaunch * self,XfdashboardFocusable * inSource,const gchar * inAction,ClutterEvent * inEvent)2322 static gboolean _xfdashboard_quicklaunch_favourite_reorder_down(XfdashboardQuicklaunch *self,
2323 																XfdashboardFocusable *inSource,
2324 																const gchar *inAction,
2325 																ClutterEvent *inEvent)
2326 {
2327 	return(_xfdashboard_quicklaunch_favourite_reorder_selection(self, XFDASHBOARD_ORIENTATION_BOTTOM));
2328 }
2329 
2330 /* An application was started or quitted */
_xfdashboard_quicklaunch_on_app_tracker_state_changed(XfdashboardQuicklaunch * self,const gchar * inDesktopID,gboolean inIsRunning,gpointer inUserData)2331 static void _xfdashboard_quicklaunch_on_app_tracker_state_changed(XfdashboardQuicklaunch *self,
2332 																	const gchar *inDesktopID,
2333 																	gboolean inIsRunning,
2334 																	gpointer inUserData)
2335 {
2336 	XfdashboardQuicklaunchPrivate	*priv;
2337 	GAppInfo						*appInfo;
2338 	ClutterActor					*actor;
2339 
2340 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
2341 	g_return_if_fail(XFDASHBOARD_IS_APPLICATION_TRACKER(inUserData));
2342 
2343 	priv=self->priv;
2344 
2345 	/* Get application information for desktop id */
2346 	appInfo=xfdashboard_application_database_lookup_desktop_id(priv->appDB, inDesktopID);
2347 	if(!appInfo)
2348 	{
2349 		XFDASHBOARD_DEBUG(self, APPLICATIONS,
2350 							"Unknown desktop ID '%s' changed state to '%s'",
2351 							inDesktopID,
2352 							inIsRunning ? "running" : "stopped");
2353 		return;
2354 	}
2355 
2356 	/* If application is now running and no actor exists for it,
2357 	 * then create an application button but mark it as dynamically added.
2358 	 */
2359 	if(inIsRunning)
2360 	{
2361 		/* Find actor for application which changed running state */
2362 		actor=_xfdashboard_quicklaunch_get_actor_for_appinfo(self, appInfo);
2363 
2364 		/* Create application button, mark it as dynamically added and
2365 		 * add it to quicklaunch.
2366 		 */
2367 		if(!actor)
2368 		{
2369 			actor=_xfdashboard_quicklaunch_create_dynamic_actor(self, appInfo);
2370 			clutter_actor_show(actor);
2371 			clutter_actor_add_child(CLUTTER_ACTOR(self), actor);
2372 
2373 			XFDASHBOARD_DEBUG(self, ACTOR,
2374 								"Created dynamic actor %p for newly running desktop ID '%s'",
2375 								actor,
2376 								inDesktopID);
2377 		}
2378 	}
2379 
2380 	/* If application is now stopped and an actor exists for it and it is
2381 	 * marked as dynamically added, then destroy it.
2382 	 */
2383 	if(!inIsRunning)
2384 	{
2385 		/* Find actor for application which changed running state */
2386 		actor=_xfdashboard_quicklaunch_get_actor_for_appinfo(self, appInfo);
2387 
2388 		/* If actor exists and is marked as dynamically added, destroy it */
2389 		if(actor &&
2390 			xfdashboard_stylable_has_class(XFDASHBOARD_STYLABLE(actor), "dynamic-app"))
2391 		{
2392 			XFDASHBOARD_DEBUG(self, ACTOR,
2393 								"Destroying dynamic actor %p for stopped desktop ID '%s'",
2394 								actor,
2395 								inDesktopID);
2396 
2397 			xfdashboard_actor_destroy(actor);
2398 		}
2399 	}
2400 }
2401 
2402 /* IMPLEMENTATION: ClutterActor */
2403 
2404 /* Get preferred width/height */
_xfdashboard_quicklaunch_get_preferred_height(ClutterActor * inActor,gfloat inForWidth,gfloat * outMinHeight,gfloat * outNaturalHeight)2405 static void _xfdashboard_quicklaunch_get_preferred_height(ClutterActor *inActor,
2406 															gfloat inForWidth,
2407 															gfloat *outMinHeight,
2408 															gfloat *outNaturalHeight)
2409 {
2410 	XfdashboardQuicklaunch			*self=XFDASHBOARD_QUICKLAUNCH(inActor);
2411 	XfdashboardQuicklaunchPrivate	*priv=self->priv;
2412 	gfloat							minHeight, naturalHeight;
2413 	ClutterActor					*child;
2414 	ClutterActorIter				iter;
2415 	gfloat							childMinHeight, childNaturalHeight;
2416 	gint							numberChildren;
2417 	gfloat							scale;
2418 
2419 	/* Set up default values */
2420 	minHeight=naturalHeight=0.0f;
2421 
2422 	/* Determine height for horizontal orientation ... */
2423 	if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)
2424 	{
2425 		/* Iterate through visible children and determine heights */
2426 		numberChildren=0;
2427 		clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inActor));
2428 		while(clutter_actor_iter_next(&iter, &child))
2429 		{
2430 			/* Only check visible children */
2431 			if(!clutter_actor_is_visible(child)) continue;
2432 
2433 			/* Get sizes of child */
2434 			clutter_actor_get_preferred_height(child,
2435 												-1,
2436 												&childMinHeight,
2437 												&childNaturalHeight);
2438 
2439 			/* Determine heights */
2440 			minHeight=MAX(minHeight, childMinHeight);
2441 			naturalHeight=MAX(naturalHeight, childNaturalHeight);
2442 
2443 			/* Count visible children */
2444 			numberChildren++;
2445 		}
2446 
2447 		/* Check if we need to scale width because of the need to fit
2448 		 * all visible children into given limiting width
2449 		 */
2450 		if(inForWidth>=0.0f)
2451 		{
2452 			scale=_xfdashboard_quicklaunch_get_scale_for_width(self, inForWidth, TRUE);
2453 			minHeight*=scale;
2454 
2455 			scale=_xfdashboard_quicklaunch_get_scale_for_width(self, inForWidth, FALSE);
2456 			naturalHeight*=scale;
2457 		}
2458 
2459 		/* Add spacing as padding */
2460 		if(numberChildren>0)
2461 		{
2462 			minHeight+=2*priv->spacing;
2463 			naturalHeight+=2*priv->spacing;
2464 		}
2465 	}
2466 		/* ... otherwise determine height for vertical orientation */
2467 		else
2468 		{
2469 			/* Iterate through visible children and determine height */
2470 			numberChildren=0;
2471 			clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inActor));
2472 			while(clutter_actor_iter_next(&iter, &child))
2473 			{
2474 				/* Only check visible children */
2475 				if(!clutter_actor_is_visible(child)) continue;
2476 
2477 				/* Get child's size */
2478 				clutter_actor_get_preferred_height(child,
2479 													inForWidth,
2480 													&childMinHeight,
2481 													&childNaturalHeight);
2482 
2483 				/* Determine heights */
2484 				minHeight+=childMinHeight;
2485 				naturalHeight+=childNaturalHeight;
2486 
2487 				/* Count visible children */
2488 				numberChildren++;
2489 			}
2490 
2491 			/* Add spacing between children and spacing as padding */
2492 			if(numberChildren>0)
2493 			{
2494 				minHeight+=(numberChildren+1)*priv->spacing;
2495 				naturalHeight+=(numberChildren+1)*priv->spacing;
2496 			}
2497 		}
2498 
2499 	/* Store sizes computed */
2500 	if(outMinHeight) *outMinHeight=minHeight;
2501 	if(outNaturalHeight) *outNaturalHeight=naturalHeight;
2502 }
2503 
_xfdashboard_quicklaunch_get_preferred_width(ClutterActor * inActor,gfloat inForHeight,gfloat * outMinWidth,gfloat * outNaturalWidth)2504 static void _xfdashboard_quicklaunch_get_preferred_width(ClutterActor *inActor,
2505 															gfloat inForHeight,
2506 															gfloat *outMinWidth,
2507 															gfloat *outNaturalWidth)
2508 {
2509 	XfdashboardQuicklaunch			*self=XFDASHBOARD_QUICKLAUNCH(inActor);
2510 	XfdashboardQuicklaunchPrivate	*priv=self->priv;
2511 	gfloat							minWidth, naturalWidth;
2512 	ClutterActor					*child;
2513 	ClutterActorIter				iter;
2514 	gfloat							childMinWidth, childNaturalWidth;
2515 	gint							numberChildren;
2516 	gfloat							scale;
2517 
2518 	/* Set up default values */
2519 	minWidth=naturalWidth=0.0f;
2520 
2521 	/* Determine width for horizontal orientation ... */
2522 	if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)
2523 	{
2524 		/* Iterate through visible children and determine width */
2525 		numberChildren=0;
2526 		clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inActor));
2527 		while(clutter_actor_iter_next(&iter, &child))
2528 		{
2529 			/* Only check visible children */
2530 			if(!clutter_actor_is_visible(child)) continue;
2531 
2532 			/* Get child's size */
2533 			clutter_actor_get_preferred_width(child,
2534 												inForHeight,
2535 												&childMinWidth,
2536 												&childNaturalWidth);
2537 
2538 			/* Determine widths */
2539 			minWidth+=childMinWidth;
2540 			naturalWidth+=childNaturalWidth;
2541 
2542 			/* Count visible children */
2543 			numberChildren++;
2544 		}
2545 
2546 		/* Add spacing between children and spacing as padding */
2547 		if(numberChildren>0)
2548 		{
2549 			minWidth+=(numberChildren+1)*priv->spacing;
2550 			naturalWidth+=(numberChildren+1)*priv->spacing;
2551 		}
2552 	}
2553 		/* ... otherwise determine width for vertical orientation */
2554 		else
2555 		{
2556 			/* Iterate through visible children and determine widths */
2557 			numberChildren=0;
2558 			clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inActor));
2559 			while(clutter_actor_iter_next(&iter, &child))
2560 			{
2561 				/* Only check visible children */
2562 				if(!clutter_actor_is_visible(child)) continue;
2563 
2564 				/* Get sizes of child */
2565 				clutter_actor_get_preferred_width(child,
2566 													-1,
2567 													&childMinWidth,
2568 													&childNaturalWidth);
2569 
2570 				/* Determine widths */
2571 				minWidth=MAX(minWidth, childMinWidth);
2572 				naturalWidth=MAX(naturalWidth, childNaturalWidth);
2573 
2574 				/* Count visible children */
2575 				numberChildren++;
2576 			}
2577 
2578 			/* Check if we need to scale width because of the need to fit
2579 			 * all visible children into given limiting height
2580 			 */
2581 			if(inForHeight>=0.0f)
2582 			{
2583 				scale=_xfdashboard_quicklaunch_get_scale_for_height(self, inForHeight, TRUE);
2584 				minWidth*=scale;
2585 
2586 				scale=_xfdashboard_quicklaunch_get_scale_for_height(self, inForHeight, FALSE);
2587 				naturalWidth*=scale;
2588 			}
2589 
2590 			/* Add spacing as padding */
2591 			if(numberChildren>0)
2592 			{
2593 				minWidth+=2*priv->spacing;
2594 				naturalWidth+=2*priv->spacing;
2595 			}
2596 		}
2597 
2598 	/* Store sizes computed */
2599 	if(outMinWidth) *outMinWidth=minWidth;
2600 	if(outNaturalWidth) *outNaturalWidth=naturalWidth;
2601 }
2602 
2603 /* Allocate position and size of actor and its children */
_xfdashboard_quicklaunch_allocate(ClutterActor * inActor,const ClutterActorBox * inBox,ClutterAllocationFlags inFlags)2604 static void _xfdashboard_quicklaunch_allocate(ClutterActor *inActor,
2605 												const ClutterActorBox *inBox,
2606 												ClutterAllocationFlags inFlags)
2607 {
2608 	XfdashboardQuicklaunch			*self=XFDASHBOARD_QUICKLAUNCH(inActor);
2609 	XfdashboardQuicklaunchPrivate	*priv=self->priv;
2610 	gfloat							availableWidth, availableHeight;
2611 	gfloat							childWidth, childHeight;
2612 	ClutterActor					*child;
2613 	ClutterActorIter				iter;
2614 	ClutterActorBox					childAllocation={ 0, };
2615 	ClutterActorBox					oldChildAllocation={ 0, };
2616 	gboolean						fixedPosition;
2617 	gfloat							fixedX, fixedY;
2618 
2619 	/* Chain up to store the allocation of the actor */
2620 	CLUTTER_ACTOR_CLASS(xfdashboard_quicklaunch_parent_class)->allocate(inActor, inBox, inFlags);
2621 
2622 	/* Get available size */
2623 	clutter_actor_box_get_size(inBox, &availableWidth, &availableHeight);
2624 
2625 	/* Find scaling to get all children fit the allocation */
2626 	priv->scaleCurrent=_xfdashboard_quicklaunch_get_scale_for_height(self, availableHeight, FALSE);
2627 
2628 	/* Calculate new position and size of visible children */
2629 	childAllocation.x1=childAllocation.y1=priv->spacing;
2630 	clutter_actor_iter_init(&iter, CLUTTER_ACTOR(inActor));
2631 	while(clutter_actor_iter_next(&iter, &child))
2632 	{
2633 		/* Is child visible? */
2634 		if(!clutter_actor_is_visible(child)) continue;
2635 
2636 		/* Calculate new position and size of child. Because we will set
2637 		 * a scale factor to the actor we have to set the real unscaled
2638 		 * width and height but the position should be "translated" to
2639 		 * scaled sizes.
2640 		 */
2641 		clutter_actor_get_preferred_size(child, NULL, NULL, &childWidth, &childHeight);
2642 
2643 		if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)
2644 		{
2645 			childAllocation.y1=ceil(MAX(((availableHeight-(childHeight*priv->scaleCurrent))/2.0f), priv->spacing));
2646 			childAllocation.y2=ceil(childAllocation.y1+childHeight);
2647 			childAllocation.x2=ceil(childAllocation.x1+childWidth);
2648 		}
2649 			else
2650 			{
2651 				childAllocation.x1=ceil(MAX(((availableWidth-(childWidth*priv->scaleCurrent))/2.0f), priv->spacing));
2652 				childAllocation.x2=ceil(childAllocation.x1+childWidth);
2653 				childAllocation.y2=ceil(childAllocation.y1+childHeight);
2654 			}
2655 
2656 		clutter_actor_set_scale(child, priv->scaleCurrent, priv->scaleCurrent);
2657 
2658 		/* Respect fixed position of child */
2659 		g_object_get(child,
2660 						"fixed-position-set", &fixedPosition,
2661 						"fixed-x", &fixedX,
2662 						"fixed-y", &fixedY,
2663 						NULL);
2664 
2665 		if(fixedPosition)
2666 		{
2667 			oldChildAllocation.x1=childAllocation.x1;
2668 			oldChildAllocation.x2=childAllocation.x2;
2669 			oldChildAllocation.y1=childAllocation.y1;
2670 			oldChildAllocation.y2=childAllocation.y2;
2671 
2672 			childWidth=childAllocation.x2-childAllocation.x1;
2673 			childHeight=childAllocation.y2-childAllocation.y1;
2674 
2675 			childAllocation.x1=ceil(fixedX);
2676 			childAllocation.x2=childAllocation.x1+childWidth;
2677 			childAllocation.y1=ceil(fixedY);
2678 			childAllocation.y2=childAllocation.y1+childHeight;
2679 		}
2680 
2681 		/* Allocate child */
2682 		clutter_actor_allocate(child, &childAllocation, inFlags);
2683 
2684 		/* Set up for next child */
2685 		if(fixedPosition)
2686 		{
2687 			childAllocation.x1=oldChildAllocation.x1;
2688 			childAllocation.x2=oldChildAllocation.x2;
2689 			childAllocation.y1=oldChildAllocation.y1;
2690 			childAllocation.y2=oldChildAllocation.y2;
2691 		}
2692 
2693 		childWidth*=priv->scaleCurrent;
2694 		childHeight*=priv->scaleCurrent;
2695 		if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL) childAllocation.x1=ceil(childAllocation.x1+childWidth+priv->spacing);
2696 			else childAllocation.y1=ceil(childAllocation.y1+childHeight+priv->spacing);
2697 	}
2698 }
2699 
2700 /* IMPLEMENTATION: Interface XfdashboardFocusable */
2701 
2702 /* Determine if this actor supports selection */
_xfdashboard_quicklaunch_focusable_supports_selection(XfdashboardFocusable * inFocusable)2703 static gboolean _xfdashboard_quicklaunch_focusable_supports_selection(XfdashboardFocusable *inFocusable)
2704 {
2705 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
2706 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inFocusable), FALSE);
2707 
2708 	/* This actor supports selection */
2709 	return(TRUE);
2710 }
2711 
2712 
2713 /* Get current selection */
_xfdashboard_quicklaunch_focusable_get_selection(XfdashboardFocusable * inFocusable)2714 static ClutterActor* _xfdashboard_quicklaunch_focusable_get_selection(XfdashboardFocusable *inFocusable)
2715 {
2716 	XfdashboardQuicklaunch				*self;
2717 	XfdashboardQuicklaunchPrivate		*priv;
2718 
2719 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), CLUTTER_EVENT_PROPAGATE);
2720 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inFocusable), CLUTTER_EVENT_PROPAGATE);
2721 
2722 	self=XFDASHBOARD_QUICKLAUNCH(inFocusable);
2723 	priv=self->priv;
2724 
2725 	/* Return current selection */
2726 	return(priv->selectedItem);
2727 }
2728 
2729 /* Set new selection */
_xfdashboard_quicklaunch_focusable_set_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection)2730 static gboolean _xfdashboard_quicklaunch_focusable_set_selection(XfdashboardFocusable *inFocusable,
2731 																	ClutterActor *inSelection)
2732 {
2733 	XfdashboardQuicklaunch				*self;
2734 	XfdashboardQuicklaunchPrivate		*priv;
2735 
2736 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
2737 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inFocusable), FALSE);
2738 	g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), FALSE);
2739 
2740 	self=XFDASHBOARD_QUICKLAUNCH(inFocusable);
2741 	priv=self->priv;
2742 
2743 	/* Check that selection is a child of this actor */
2744 	if(inSelection &&
2745 		!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
2746 	{
2747 		ClutterActor						*parent;
2748 
2749 		parent=clutter_actor_get_parent(inSelection);
2750 		g_warning("%s is a child of %s and cannot be selected at %s",
2751 					G_OBJECT_TYPE_NAME(inSelection),
2752 					parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>",
2753 					G_OBJECT_TYPE_NAME(self));
2754 	}
2755 
2756 	/* Set new selection */
2757 	priv->selectedItem=inSelection;
2758 
2759 	/* New selection was set successfully */
2760 	return(TRUE);
2761 }
2762 
2763 /* Find requested selection target depending of current selection */
_xfdashboard_quicklaunch_focusable_find_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection,XfdashboardSelectionTarget inDirection)2764 static ClutterActor* _xfdashboard_quicklaunch_focusable_find_selection(XfdashboardFocusable *inFocusable,
2765 																		ClutterActor *inSelection,
2766 																		XfdashboardSelectionTarget inDirection)
2767 {
2768 	XfdashboardQuicklaunch				*self;
2769 	XfdashboardQuicklaunchPrivate		*priv;
2770 	ClutterActor						*selection;
2771 	ClutterActor						*newSelection;
2772 	gchar								*valueName;
2773 
2774 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
2775 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inFocusable), FALSE);
2776 	g_return_val_if_fail(!inSelection || CLUTTER_IS_ACTOR(inSelection), NULL);
2777 	g_return_val_if_fail(inDirection>=0 && inDirection<=XFDASHBOARD_SELECTION_TARGET_NEXT, NULL);
2778 
2779 	self=XFDASHBOARD_QUICKLAUNCH(inFocusable);
2780 	priv=self->priv;
2781 	selection=inSelection;
2782 	newSelection=NULL;
2783 
2784 	/* If there is nothing selected, select first actor and return */
2785 	if(!inSelection)
2786 	{
2787 		selection=clutter_actor_get_first_child(CLUTTER_ACTOR(self));
2788 
2789 		valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
2790 		XFDASHBOARD_DEBUG(self, ACTOR,
2791 							"No selection at %s, so select first child %s for direction %s",
2792 							G_OBJECT_TYPE_NAME(self),
2793 							selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
2794 							valueName);
2795 		g_free(valueName);
2796 
2797 		return(selection);
2798 	}
2799 
2800 	/* Check that selection is a child of this actor otherwise return NULL */
2801 	if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
2802 	{
2803 		ClutterActor						*parent;
2804 
2805 		parent=clutter_actor_get_parent(inSelection);
2806 		g_warning("Cannot lookup selection target at %s because %s is a child of %s",
2807 					G_OBJECT_TYPE_NAME(self),
2808 					G_OBJECT_TYPE_NAME(inSelection),
2809 					parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>");
2810 
2811 		return(NULL);
2812 	}
2813 
2814 	/* Find target selection */
2815 	switch(inDirection)
2816 	{
2817 		case XFDASHBOARD_SELECTION_TARGET_LEFT:
2818 			if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)
2819 			{
2820 				newSelection=xfdashboard_quicklaunch_get_previous_selectable(self, inSelection);
2821 			}
2822 			break;
2823 
2824 		case XFDASHBOARD_SELECTION_TARGET_RIGHT:
2825 			if(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL)
2826 			{
2827 				newSelection=xfdashboard_quicklaunch_get_next_selectable(self, inSelection);
2828 			}
2829 			break;
2830 
2831 		case XFDASHBOARD_SELECTION_TARGET_UP:
2832 			if(priv->orientation==CLUTTER_ORIENTATION_VERTICAL)
2833 			{
2834 				newSelection=xfdashboard_quicklaunch_get_previous_selectable(self, inSelection);
2835 			}
2836 			break;
2837 
2838 		case XFDASHBOARD_SELECTION_TARGET_DOWN:
2839 			if(priv->orientation==CLUTTER_ORIENTATION_VERTICAL)
2840 			{
2841 				newSelection=xfdashboard_quicklaunch_get_next_selectable(self, inSelection);
2842 			}
2843 			break;
2844 
2845 		case XFDASHBOARD_SELECTION_TARGET_FIRST:
2846 		case XFDASHBOARD_SELECTION_TARGET_PAGE_UP:
2847 		case XFDASHBOARD_SELECTION_TARGET_PAGE_LEFT:
2848 			if(inDirection==XFDASHBOARD_SELECTION_TARGET_FIRST ||
2849 				(inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_UP && priv->orientation==CLUTTER_ORIENTATION_VERTICAL) ||
2850 				(inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_LEFT && priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL))
2851 			{
2852 				newSelection=clutter_actor_get_first_child(CLUTTER_ACTOR(self));
2853 				while(newSelection && !clutter_actor_is_visible(newSelection))
2854 				{
2855 					newSelection=clutter_actor_get_next_sibling(newSelection);
2856 				}
2857 			}
2858 			break;
2859 
2860 		case XFDASHBOARD_SELECTION_TARGET_LAST:
2861 		case XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN:
2862 		case XFDASHBOARD_SELECTION_TARGET_PAGE_RIGHT:
2863 			if(inDirection==XFDASHBOARD_SELECTION_TARGET_LAST ||
2864 				(inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_DOWN && priv->orientation==CLUTTER_ORIENTATION_VERTICAL) ||
2865 				(inDirection==XFDASHBOARD_SELECTION_TARGET_PAGE_RIGHT && priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL))
2866 			{
2867 				newSelection=clutter_actor_get_last_child(CLUTTER_ACTOR(self));
2868 				while(newSelection && !clutter_actor_is_visible(newSelection))
2869 				{
2870 					newSelection=clutter_actor_get_previous_sibling(newSelection);
2871 				}
2872 			}
2873 			break;
2874 
2875 		case XFDASHBOARD_SELECTION_TARGET_NEXT:
2876 			newSelection=xfdashboard_quicklaunch_get_next_selectable(self, inSelection);
2877 			while(newSelection && !clutter_actor_is_visible(newSelection))
2878 			{
2879 				newSelection=clutter_actor_get_next_sibling(newSelection);
2880 			}
2881 
2882 			if(!newSelection)
2883 			{
2884 				newSelection=xfdashboard_quicklaunch_get_previous_selectable(self, inSelection);
2885 				while(newSelection && !clutter_actor_is_visible(newSelection))
2886 				{
2887 					newSelection=clutter_actor_get_next_sibling(newSelection);
2888 				}
2889 			}
2890 			break;
2891 
2892 		default:
2893 			{
2894 				valueName=xfdashboard_get_enum_value_name(XFDASHBOARD_TYPE_SELECTION_TARGET, inDirection);
2895 				g_critical("Focusable object %s does not handle selection direction of type %s.",
2896 							G_OBJECT_TYPE_NAME(self),
2897 							valueName);
2898 				g_free(valueName);
2899 			}
2900 			break;
2901 	}
2902 
2903 	/* If new selection could be found override current selection with it */
2904 	if(newSelection) selection=newSelection;
2905 
2906 	/* Return new selection found */
2907 	XFDASHBOARD_DEBUG(self, ACTOR,
2908 						"Selecting %s at %s for current selection %s in direction %u",
2909 						selection ? G_OBJECT_TYPE_NAME(selection) : "<nil>",
2910 						G_OBJECT_TYPE_NAME(self),
2911 						inSelection ? G_OBJECT_TYPE_NAME(inSelection) : "<nil>",
2912 						inDirection);
2913 
2914 	return(selection);
2915 }
2916 
2917 /* Activate selection */
_xfdashboard_quicklaunch_focusable_activate_selection(XfdashboardFocusable * inFocusable,ClutterActor * inSelection)2918 static gboolean _xfdashboard_quicklaunch_focusable_activate_selection(XfdashboardFocusable *inFocusable,
2919 																		ClutterActor *inSelection)
2920 {
2921 	XfdashboardQuicklaunch				*self;
2922 
2923 	g_return_val_if_fail(XFDASHBOARD_IS_FOCUSABLE(inFocusable), FALSE);
2924 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(inFocusable), FALSE);
2925 	g_return_val_if_fail(CLUTTER_IS_ACTOR(inSelection), FALSE);
2926 
2927 	self=XFDASHBOARD_QUICKLAUNCH(inFocusable);
2928 
2929 	/* Check that selection is a child of this actor */
2930 	if(!clutter_actor_contains(CLUTTER_ACTOR(self), inSelection))
2931 	{
2932 		ClutterActor						*parent;
2933 
2934 		parent=clutter_actor_get_parent(inSelection);
2935 		g_warning("%s is a child of %s and cannot be activated at %s",
2936 					G_OBJECT_TYPE_NAME(inSelection),
2937 					parent ? G_OBJECT_TYPE_NAME(parent) : "<nil>",
2938 					G_OBJECT_TYPE_NAME(self));
2939 
2940 		return(FALSE);
2941 	}
2942 
2943 	/* Activate selection */
2944 	g_signal_emit_by_name(inSelection, "clicked");
2945 
2946 	return(TRUE);
2947 }
2948 
2949 /* Interface initialization
2950  * Set up default functions
2951  */
_xfdashboard_quicklaunch_focusable_iface_init(XfdashboardFocusableInterface * iface)2952 void _xfdashboard_quicklaunch_focusable_iface_init(XfdashboardFocusableInterface *iface)
2953 {
2954 	iface->supports_selection=_xfdashboard_quicklaunch_focusable_supports_selection;
2955 	iface->get_selection=_xfdashboard_quicklaunch_focusable_get_selection;
2956 	iface->set_selection=_xfdashboard_quicklaunch_focusable_set_selection;
2957 	iface->find_selection=_xfdashboard_quicklaunch_focusable_find_selection;
2958 	iface->activate_selection=_xfdashboard_quicklaunch_focusable_activate_selection;
2959 }
2960 
2961 /* IMPLEMENTATION: GObject */
2962 
2963 /* Dispose this object */
_xfdashboard_quicklaunch_dispose(GObject * inObject)2964 static void _xfdashboard_quicklaunch_dispose(GObject *inObject)
2965 {
2966 	XfdashboardQuicklaunchPrivate	*priv=XFDASHBOARD_QUICKLAUNCH(inObject)->priv;
2967 
2968 	/* Release our allocated variables */
2969 	if(priv->xfconfFavouritesBindingID)
2970 	{
2971 		xfconf_g_property_unbind(priv->xfconfFavouritesBindingID);
2972 		priv->xfconfFavouritesBindingID=0;
2973 	}
2974 
2975 	if(priv->xfconfChannel)
2976 	{
2977 		priv->xfconfChannel=NULL;
2978 	}
2979 
2980 	if(priv->appTracker)
2981 	{
2982 		g_object_unref(priv->appTracker);
2983 		priv->appTracker=NULL;
2984 	}
2985 
2986 	if(priv->appDB)
2987 	{
2988 		g_object_unref(priv->appDB);
2989 		priv->appDB=NULL;
2990 	}
2991 
2992 	if(priv->favourites)
2993 	{
2994 		xfconf_array_free(priv->favourites);
2995 		priv->favourites=NULL;
2996 	}
2997 
2998 	if(priv->separatorFavouritesToDynamic)
2999 	{
3000 		clutter_actor_destroy(priv->separatorFavouritesToDynamic);
3001 		priv->separatorFavouritesToDynamic=NULL;
3002 	}
3003 
3004 	/* Call parent's class dispose method */
3005 	G_OBJECT_CLASS(xfdashboard_quicklaunch_parent_class)->dispose(inObject);
3006 }
3007 
3008 /* Set/get properties */
_xfdashboard_quicklaunch_set_property(GObject * inObject,guint inPropID,const GValue * inValue,GParamSpec * inSpec)3009 static void _xfdashboard_quicklaunch_set_property(GObject *inObject,
3010 													guint inPropID,
3011 													const GValue *inValue,
3012 													GParamSpec *inSpec)
3013 {
3014 	XfdashboardQuicklaunch			*self=XFDASHBOARD_QUICKLAUNCH(inObject);
3015 
3016 	switch(inPropID)
3017 	{
3018 		case PROP_FAVOURITES:
3019 			_xfdashboard_quicklaunch_set_favourites(self, inValue);
3020 			break;
3021 
3022 		case PROP_NORMAL_ICON_SIZE:
3023 			xfdashboard_quicklaunch_set_normal_icon_size(self, g_value_get_float(inValue));
3024 			break;
3025 
3026 		case PROP_SPACING:
3027 			xfdashboard_quicklaunch_set_spacing(self, g_value_get_float(inValue));
3028 			break;
3029 
3030 		case PROP_ORIENTATION:
3031 			xfdashboard_quicklaunch_set_orientation(self, g_value_get_enum(inValue));
3032 			break;
3033 
3034 		default:
3035 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
3036 			break;
3037 	}
3038 }
3039 
_xfdashboard_quicklaunch_get_property(GObject * inObject,guint inPropID,GValue * outValue,GParamSpec * inSpec)3040 static void _xfdashboard_quicklaunch_get_property(GObject *inObject,
3041 													guint inPropID,
3042 													GValue *outValue,
3043 													GParamSpec *inSpec)
3044 {
3045 	XfdashboardQuicklaunch			*self=XFDASHBOARD_QUICKLAUNCH(inObject);
3046 	XfdashboardQuicklaunchPrivate	*priv=self->priv;
3047 
3048 	switch(inPropID)
3049 	{
3050 		case PROP_FAVOURITES:
3051 			g_value_set_boxed(outValue, priv->favourites);
3052 			break;
3053 
3054 		case PROP_NORMAL_ICON_SIZE:
3055 			g_value_set_float(outValue, priv->normalIconSize);
3056 			break;
3057 
3058 		case PROP_SPACING:
3059 			g_value_set_float(outValue, priv->spacing);
3060 			break;
3061 
3062 		case PROP_ORIENTATION:
3063 			g_value_set_enum(outValue, priv->orientation);
3064 			break;
3065 
3066 		default:
3067 			G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
3068 			break;
3069 	}
3070 }
3071 
3072 /* Class initialization
3073  * Override functions in parent classes and define properties
3074  * and signals
3075  */
xfdashboard_quicklaunch_class_init(XfdashboardQuicklaunchClass * klass)3076 static void xfdashboard_quicklaunch_class_init(XfdashboardQuicklaunchClass *klass)
3077 {
3078 	XfdashboardActorClass	*actorClass=XFDASHBOARD_ACTOR_CLASS(klass);
3079 	ClutterActorClass		*clutterActorClass=CLUTTER_ACTOR_CLASS(klass);
3080 	GObjectClass			*gobjectClass=G_OBJECT_CLASS(klass);
3081 
3082 	/* Override functions */
3083 	gobjectClass->dispose=_xfdashboard_quicklaunch_dispose;
3084 	gobjectClass->set_property=_xfdashboard_quicklaunch_set_property;
3085 	gobjectClass->get_property=_xfdashboard_quicklaunch_get_property;
3086 
3087 	clutterActorClass->get_preferred_width=_xfdashboard_quicklaunch_get_preferred_width;
3088 	clutterActorClass->get_preferred_height=_xfdashboard_quicklaunch_get_preferred_height;
3089 	clutterActorClass->allocate=_xfdashboard_quicklaunch_allocate;
3090 
3091 	klass->selection_add_favourite=_xfdashboard_quicklaunch_selection_add_favourite;
3092 	klass->selection_remove_favourite=_xfdashboard_quicklaunch_selection_remove_favourite;
3093 	klass->favourite_reorder_left=_xfdashboard_quicklaunch_favourite_reorder_left;
3094 	klass->favourite_reorder_right=_xfdashboard_quicklaunch_favourite_reorder_right;
3095 	klass->favourite_reorder_up=_xfdashboard_quicklaunch_favourite_reorder_up;
3096 	klass->favourite_reorder_down=_xfdashboard_quicklaunch_favourite_reorder_down;
3097 
3098 	/* Define properties */
3099 	XfdashboardQuicklaunchProperties[PROP_FAVOURITES]=
3100 		g_param_spec_boxed("favourites",
3101 							"Favourites",
3102 							"An array of strings pointing to desktop files shown as icons",
3103 							XFDASHBOARD_TYPE_POINTER_ARRAY,
3104 							G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
3105 
3106 	XfdashboardQuicklaunchProperties[PROP_NORMAL_ICON_SIZE]=
3107 		g_param_spec_float("normal-icon-size",
3108 								"Normal icon size",
3109 								"Unscale size of icon",
3110 								1.0, G_MAXFLOAT,
3111 								1.0f,
3112 								G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
3113 
3114 	XfdashboardQuicklaunchProperties[PROP_SPACING]=
3115 		g_param_spec_float("spacing",
3116 								"Spacing",
3117 								"The spacing between children",
3118 								0.0, G_MAXFLOAT,
3119 								0.0,
3120 								G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
3121 
3122 	XfdashboardQuicklaunchProperties[PROP_ORIENTATION]=
3123 		g_param_spec_enum("orientation",
3124 							"Orientation",
3125 							"The orientation to layout children",
3126 							CLUTTER_TYPE_ORIENTATION,
3127 							CLUTTER_ORIENTATION_VERTICAL,
3128 							G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
3129 
3130 	g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardQuicklaunchProperties);
3131 
3132 	/* Define stylable properties */
3133 	xfdashboard_actor_install_stylable_property(actorClass, XfdashboardQuicklaunchProperties[PROP_NORMAL_ICON_SIZE]);
3134 	xfdashboard_actor_install_stylable_property(actorClass, XfdashboardQuicklaunchProperties[PROP_SPACING]);
3135 	xfdashboard_actor_install_stylable_property(actorClass, XfdashboardQuicklaunchProperties[PROP_ORIENTATION]);
3136 
3137 	/* Define signals */
3138 	XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_ADDED]=
3139 		g_signal_new("favourite-added",
3140 						G_TYPE_FROM_CLASS(klass),
3141 						G_SIGNAL_RUN_LAST,
3142 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_added),
3143 						NULL,
3144 						NULL,
3145 						g_cclosure_marshal_VOID__OBJECT,
3146 						G_TYPE_NONE,
3147 						1,
3148 						G_TYPE_APP_INFO);
3149 
3150 	XfdashboardQuicklaunchSignals[SIGNAL_FAVOURITE_REMOVED]=
3151 		g_signal_new("favourite-removed",
3152 						G_TYPE_FROM_CLASS(klass),
3153 						G_SIGNAL_RUN_LAST,
3154 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_removed),
3155 						NULL,
3156 						NULL,
3157 						g_cclosure_marshal_VOID__OBJECT,
3158 						G_TYPE_NONE,
3159 						1,
3160 						G_TYPE_APP_INFO);
3161 
3162 	XfdashboardQuicklaunchSignals[ACTION_SELECTION_ADD_FAVOURITE]=
3163 		g_signal_new("selection-add-favourite",
3164 						G_TYPE_FROM_CLASS(klass),
3165 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3166 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, selection_add_favourite),
3167 						g_signal_accumulator_true_handled,
3168 						NULL,
3169 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3170 						G_TYPE_BOOLEAN,
3171 						3,
3172 						XFDASHBOARD_TYPE_FOCUSABLE,
3173 						G_TYPE_STRING,
3174 						CLUTTER_TYPE_EVENT);
3175 
3176 	XfdashboardQuicklaunchSignals[ACTION_SELECTION_REMOVE_FAVOURITE]=
3177 		g_signal_new("selection-remove-favourite",
3178 						G_TYPE_FROM_CLASS(klass),
3179 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3180 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, selection_remove_favourite),
3181 						g_signal_accumulator_true_handled,
3182 						NULL,
3183 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3184 						G_TYPE_BOOLEAN,
3185 						3,
3186 						XFDASHBOARD_TYPE_FOCUSABLE,
3187 						G_TYPE_STRING,
3188 						CLUTTER_TYPE_EVENT);
3189 
3190 	XfdashboardQuicklaunchSignals[ACTION_FAVOURITE_REORDER_LEFT]=
3191 		g_signal_new("favourite-reorder-left",
3192 						G_TYPE_FROM_CLASS(klass),
3193 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3194 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_reorder_left),
3195 						g_signal_accumulator_true_handled,
3196 						NULL,
3197 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3198 						G_TYPE_BOOLEAN,
3199 						3,
3200 						XFDASHBOARD_TYPE_FOCUSABLE,
3201 						G_TYPE_STRING,
3202 						CLUTTER_TYPE_EVENT);
3203 
3204 	XfdashboardQuicklaunchSignals[ACTION_FAVOURITE_REORDER_RIGHT]=
3205 		g_signal_new("favourite-reorder-right",
3206 						G_TYPE_FROM_CLASS(klass),
3207 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3208 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_reorder_right),
3209 						g_signal_accumulator_true_handled,
3210 						NULL,
3211 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3212 						G_TYPE_BOOLEAN,
3213 						3,
3214 						XFDASHBOARD_TYPE_FOCUSABLE,
3215 						G_TYPE_STRING,
3216 						CLUTTER_TYPE_EVENT);
3217 
3218 	XfdashboardQuicklaunchSignals[ACTION_FAVOURITE_REORDER_UP]=
3219 		g_signal_new("favourite-reorder-up",
3220 						G_TYPE_FROM_CLASS(klass),
3221 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3222 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_reorder_up),
3223 						g_signal_accumulator_true_handled,
3224 						NULL,
3225 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3226 						G_TYPE_BOOLEAN,
3227 						3,
3228 						XFDASHBOARD_TYPE_FOCUSABLE,
3229 						G_TYPE_STRING,
3230 						CLUTTER_TYPE_EVENT);
3231 
3232 	XfdashboardQuicklaunchSignals[ACTION_FAVOURITE_REORDER_DOWN]=
3233 		g_signal_new("favourite-reorder-down",
3234 						G_TYPE_FROM_CLASS(klass),
3235 						G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
3236 						G_STRUCT_OFFSET(XfdashboardQuicklaunchClass, favourite_reorder_down),
3237 						g_signal_accumulator_true_handled,
3238 						NULL,
3239 						_xfdashboard_marshal_BOOLEAN__OBJECT_STRING_BOXED,
3240 						G_TYPE_BOOLEAN,
3241 						3,
3242 						XFDASHBOARD_TYPE_FOCUSABLE,
3243 						G_TYPE_STRING,
3244 						CLUTTER_TYPE_EVENT);
3245 }
3246 
3247 /* Object initialization
3248  * Create private structure and set up default values
3249  */
xfdashboard_quicklaunch_init(XfdashboardQuicklaunch * self)3250 static void xfdashboard_quicklaunch_init(XfdashboardQuicklaunch *self)
3251 {
3252 	XfdashboardQuicklaunchPrivate	*priv;
3253 	ClutterRequestMode				requestMode;
3254 	ClutterAction					*dropAction;
3255 
3256 	priv=self->priv=xfdashboard_quicklaunch_get_instance_private(self);
3257 
3258 	/* Set up default values */
3259 	priv->favourites=NULL;
3260 	priv->spacing=0.0f;
3261 	priv->orientation=CLUTTER_ORIENTATION_VERTICAL;
3262 	priv->normalIconSize=1.0f;
3263 	priv->scaleCurrent=DEFAULT_SCALE_MAX;
3264 	priv->scaleMin=DEFAULT_SCALE_MIN;
3265 	priv->scaleMax=DEFAULT_SCALE_MAX;
3266 	priv->scaleStep=DEFAULT_SCALE_STEP;
3267 	priv->xfconfChannel=xfdashboard_application_get_xfconf_channel(NULL);
3268 	priv->dragMode=DRAG_MODE_NONE;
3269 	priv->dragPreviewIcon=NULL;
3270 	priv->selectedItem=NULL;
3271 	priv->appDB=xfdashboard_application_database_get_default();
3272 
3273 	/* Set up this actor */
3274 	clutter_actor_set_reactive(CLUTTER_ACTOR(self), TRUE);
3275 	requestMode=(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
3276 	clutter_actor_set_request_mode(CLUTTER_ACTOR(self), requestMode);
3277 
3278 	dropAction=xfdashboard_drop_action_new();
3279 	clutter_actor_add_action(CLUTTER_ACTOR(self), dropAction);
3280 	g_signal_connect_swapped(dropAction, "begin", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_begin), self);
3281 	g_signal_connect_swapped(dropAction, "drop", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_drop), self);
3282 	g_signal_connect_swapped(dropAction, "end", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_end), self);
3283 	g_signal_connect_swapped(dropAction, "drag-enter", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_enter), self);
3284 	g_signal_connect_swapped(dropAction, "drag-motion", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_motion), self);
3285 	g_signal_connect_swapped(dropAction, "drag-leave", G_CALLBACK(_xfdashboard_quicklaunch_on_drop_leave), self);
3286 
3287 	/* Add "applications" button */
3288 	priv->appsButton=xfdashboard_toggle_button_new_with_text(_("Applications"));
3289 	clutter_actor_set_name(priv->appsButton, "applications-button");
3290 	xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(priv->appsButton), priv->normalIconSize);
3291 	xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(priv->appsButton), FALSE);
3292 	clutter_actor_add_child(CLUTTER_ACTOR(self), priv->appsButton);
3293 
3294 	/* Next add trash button to box but initially hidden and register as drop target */
3295 	priv->trashButton=xfdashboard_toggle_button_new_with_text(_("Remove"));
3296 	clutter_actor_set_name(priv->trashButton, "trash-button");
3297 	clutter_actor_hide(priv->trashButton);
3298 	xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(priv->trashButton), priv->normalIconSize);
3299 	xfdashboard_label_set_sync_icon_size(XFDASHBOARD_LABEL(priv->trashButton), FALSE);
3300 	clutter_actor_add_child(CLUTTER_ACTOR(self), priv->trashButton);
3301 
3302 	dropAction=xfdashboard_drop_action_new();
3303 	clutter_actor_add_action(priv->trashButton, CLUTTER_ACTION(dropAction));
3304 	g_signal_connect_swapped(dropAction, "begin", G_CALLBACK(_xfdashboard_quicklaunch_on_trash_drop_begin), self);
3305 	g_signal_connect_swapped(dropAction, "drop", G_CALLBACK(_xfdashboard_quicklaunch_on_trash_drop_drop), self);
3306 	g_signal_connect_swapped(dropAction, "end", G_CALLBACK(_xfdashboard_quicklaunch_on_trash_drop_end), self);
3307 	g_signal_connect_swapped(dropAction, "drag-enter", G_CALLBACK(_xfdashboard_quicklaunch_on_trash_drop_enter), self);
3308 	g_signal_connect_swapped(dropAction, "drag-leave", G_CALLBACK(_xfdashboard_quicklaunch_on_trash_drop_leave), self);
3309 
3310 	/* Add a hidden actor used as separator between application buttons for
3311 	 * favourites and dynamically added one (for running non-favourite applications).
3312 	 * It used to add application buttons for favourites before dynamically
3313 	 * added ones.
3314 	 */
3315 	priv->separatorFavouritesToDynamic=clutter_actor_new();
3316 	clutter_actor_hide(priv->separatorFavouritesToDynamic);
3317 	clutter_actor_add_child(CLUTTER_ACTOR(self), priv->separatorFavouritesToDynamic);
3318 
3319 	/* Bind to xfconf to react on changes */
3320 	priv->xfconfFavouritesBindingID=xfconf_g_property_bind(priv->xfconfChannel,
3321 															FAVOURITES_XFCONF_PROP,
3322 															XFDASHBOARD_TYPE_POINTER_ARRAY,
3323 															self,
3324 															"favourites");
3325 
3326 	/* Set up default favourite items if property in channel does not exist
3327 	 * because it indicates first start.
3328 	 */
3329 	if(!xfconf_channel_has_property(priv->xfconfChannel, FAVOURITES_XFCONF_PROP))
3330 	{
3331 		_xfdashboard_quicklaunch_setup_default_favourites(self);
3332 	}
3333 
3334 	/* Connect to application tracker to recognize other running application
3335 	 * which are not known favourites.
3336 	 */
3337 	priv->appTracker=xfdashboard_application_tracker_get_default();
3338 	g_signal_connect_swapped(priv->appTracker, "state-changed", G_CALLBACK(_xfdashboard_quicklaunch_on_app_tracker_state_changed), self);
3339 }
3340 
3341 /* IMPLEMENTATION: Public API */
3342 
3343 /* Create new actor */
xfdashboard_quicklaunch_new(void)3344 ClutterActor* xfdashboard_quicklaunch_new(void)
3345 {
3346 	return(g_object_new(XFDASHBOARD_TYPE_QUICKLAUNCH, NULL));
3347 }
3348 
xfdashboard_quicklaunch_new_with_orientation(ClutterOrientation inOrientation)3349 ClutterActor* xfdashboard_quicklaunch_new_with_orientation(ClutterOrientation inOrientation)
3350 {
3351 	g_return_val_if_fail(inOrientation==CLUTTER_ORIENTATION_HORIZONTAL || inOrientation==CLUTTER_ORIENTATION_VERTICAL, NULL);
3352 
3353 	return(g_object_new(XFDASHBOARD_TYPE_QUICKLAUNCH,
3354 						"orientation", inOrientation,
3355 						NULL));
3356 }
3357 
3358 /* Get/set spacing between children */
xfdashboard_quicklaunch_get_normal_icon_size(XfdashboardQuicklaunch * self)3359 gfloat xfdashboard_quicklaunch_get_normal_icon_size(XfdashboardQuicklaunch *self)
3360 {
3361 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), 0.0f);
3362 
3363 	return(self->priv->normalIconSize);
3364 }
3365 
xfdashboard_quicklaunch_set_normal_icon_size(XfdashboardQuicklaunch * self,const gfloat inIconSize)3366 void xfdashboard_quicklaunch_set_normal_icon_size(XfdashboardQuicklaunch *self, const gfloat inIconSize)
3367 {
3368 	XfdashboardQuicklaunchPrivate	*priv;
3369 
3370 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
3371 	g_return_if_fail(inIconSize>=0.0f);
3372 
3373 	priv=self->priv;
3374 
3375 	/* Set value if changed */
3376 	if(priv->normalIconSize!=inIconSize)
3377 	{
3378 		/* Set value */
3379 		priv->normalIconSize=inIconSize;
3380 		clutter_actor_queue_relayout(CLUTTER_ACTOR(self));
3381 
3382 		xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(priv->appsButton), priv->normalIconSize);
3383 		xfdashboard_label_set_icon_size(XFDASHBOARD_LABEL(priv->trashButton), priv->normalIconSize);
3384 
3385 		/* Notify about property change */
3386 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardQuicklaunchProperties[PROP_NORMAL_ICON_SIZE]);
3387 	}
3388 }
3389 
3390 /* Get/set spacing between children */
xfdashboard_quicklaunch_get_spacing(XfdashboardQuicklaunch * self)3391 gfloat xfdashboard_quicklaunch_get_spacing(XfdashboardQuicklaunch *self)
3392 {
3393 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), 0.0f);
3394 
3395 	return(self->priv->spacing);
3396 }
3397 
xfdashboard_quicklaunch_set_spacing(XfdashboardQuicklaunch * self,const gfloat inSpacing)3398 void xfdashboard_quicklaunch_set_spacing(XfdashboardQuicklaunch *self, const gfloat inSpacing)
3399 {
3400 	XfdashboardQuicklaunchPrivate	*priv;
3401 
3402 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
3403 	g_return_if_fail(inSpacing>=0.0f);
3404 
3405 	priv=self->priv;
3406 
3407 	/* Set value if changed */
3408 	if(priv->spacing!=inSpacing)
3409 	{
3410 		/* Set value */
3411 		priv->spacing=inSpacing;
3412 		clutter_actor_queue_relayout(CLUTTER_ACTOR(self));
3413 
3414 		xfdashboard_background_set_corner_radius(XFDASHBOARD_BACKGROUND(self), priv->spacing);
3415 
3416 		/* Notify about property change */
3417 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardQuicklaunchProperties[PROP_SPACING]);
3418 	}
3419 }
3420 
3421 /* Get/set orientation */
xfdashboard_quicklaunch_get_orientation(XfdashboardQuicklaunch * self)3422 ClutterOrientation xfdashboard_quicklaunch_get_orientation(XfdashboardQuicklaunch *self)
3423 {
3424 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), CLUTTER_ORIENTATION_VERTICAL);
3425 
3426 	return(self->priv->orientation);
3427 }
3428 
xfdashboard_quicklaunch_set_orientation(XfdashboardQuicklaunch * self,ClutterOrientation inOrientation)3429 void xfdashboard_quicklaunch_set_orientation(XfdashboardQuicklaunch *self, ClutterOrientation inOrientation)
3430 {
3431 	XfdashboardQuicklaunchPrivate	*priv;
3432 	ClutterRequestMode				requestMode;
3433 
3434 	g_return_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self));
3435 	g_return_if_fail(inOrientation==CLUTTER_ORIENTATION_HORIZONTAL ||
3436 						inOrientation==CLUTTER_ORIENTATION_VERTICAL);
3437 
3438 	priv=self->priv;
3439 
3440 	/* Set value if changed */
3441 	if(priv->orientation!=inOrientation)
3442 	{
3443 		/* Set value */
3444 		priv->orientation=inOrientation;
3445 
3446 		requestMode=(priv->orientation==CLUTTER_ORIENTATION_HORIZONTAL ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT);
3447 		clutter_actor_set_request_mode(CLUTTER_ACTOR(self), requestMode);
3448 
3449 		clutter_actor_queue_relayout(CLUTTER_ACTOR(self));
3450 
3451 		/* Notify about property change */
3452 		g_object_notify_by_pspec(G_OBJECT(self), XfdashboardQuicklaunchProperties[PROP_ORIENTATION]);
3453 	}
3454 }
3455 
3456 /* Get apps button */
xfdashboard_quicklaunch_get_apps_button(XfdashboardQuicklaunch * self)3457 XfdashboardToggleButton* xfdashboard_quicklaunch_get_apps_button(XfdashboardQuicklaunch *self)
3458 {
3459 	g_return_val_if_fail(XFDASHBOARD_IS_QUICKLAUNCH(self), NULL);
3460 
3461 	return(XFDASHBOARD_TOGGLE_BUTTON(self->priv->appsButton));
3462 }
3463