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