/* Find drop target at position */ static XfdashboardDropAction* _xfdashboard_drag_action_find_drop_traget_at_coord(XfdashboardDragAction *self, gfloat inStageX, gfloat inStageY) { XfdashboardDragActionPrivate *priv; GSList *list; g_return_val_if_fail(XFDASHBOARD_IS_DRAG_ACTION(self), NULL); priv=self->priv; /* Iterate through list and return first drop target in list * where coordinates fit in */ for(list=priv->targets; list; list=g_slist_next(list)) { ClutterActorMeta *actorMeta=CLUTTER_ACTOR_META(list->data); ClutterActor *actor=clutter_actor_meta_get_actor(actorMeta); gfloat x, y, w, h; /* Get position and size of actor in stage coordinates */ clutter_actor_get_transformed_position(actor, &x, &y); clutter_actor_get_transformed_size(actor, &w, &h); /* If given stage coordinates fit in actor we found it */ if(inStageX>=x && inStageX<(x+w) && inStageY>=y && inStageY<(y+h)) { return(XFDASHBOARD_DROP_ACTION(actorMeta)); } } /* If we get here no drop target was found */ return(NULL); }
/* Called when attaching and detaching a ClutterActorMeta instance to a ClutterActor */ static void _xfdashboard_drop_action_set_actor(ClutterActorMeta *inActorMeta, ClutterActor *inActor) { XfdashboardDropAction *self; XfdashboardDropActionPrivate *priv; g_return_if_fail(XFDASHBOARD_IS_DROP_ACTION(inActorMeta)); g_return_if_fail(inActor==NULL || CLUTTER_IS_ACTOR(inActor)); self=XFDASHBOARD_DROP_ACTION(inActorMeta); priv=self->priv; /* Unregister current drop target */ if(priv->actor) { /* Disconnect signals */ if(priv->destroySignalID) g_signal_handler_disconnect(priv->actor, priv->destroySignalID); /* Unset style */ if(XFDASHBOARD_IS_ACTOR(priv->actor)) { xfdashboard_stylable_remove_pseudo_class(XFDASHBOARD_STYLABLE(priv->actor), "drop-target"); } /* Unregister drop target */ _xfdashboard_drop_action_unregister_target(self); priv->destroySignalID=0; priv->actor=NULL; } /* Register new drop target */ if(inActor) { priv->actor=inActor; /* Register drop target */ _xfdashboard_drop_action_register_target(self); /* Connect signals */ priv->destroySignalID=g_signal_connect_swapped(priv->actor, "destroy", G_CALLBACK(_xfdashboard_drop_action_on_target_actor_destroy), self); } /* Call parent's class method */ CLUTTER_ACTOR_META_CLASS(xfdashboard_drop_action_parent_class)->set_actor(inActorMeta, inActor); }
/* Dragging of actor ended */ static void _xfdashboard_drag_action_drag_end(ClutterDragAction *inAction, ClutterActor *inActor, gfloat inStageX, gfloat inStageY, ClutterModifierType inModifiers) { XfdashboardDragAction *self; XfdashboardDragActionPrivate *priv; ClutterDragActionClass *dragActionClass; GSList *list; XfdashboardDropAction *dropTarget; gfloat dropX, dropY; gboolean canDrop; g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inAction)); self=XFDASHBOARD_DRAG_ACTION(inAction); priv=self->priv; dragActionClass=CLUTTER_DRAG_ACTION_CLASS(xfdashboard_drag_action_parent_class); canDrop=FALSE; dropTarget=NULL; /* Hold a reference on ourselve as ClutterAction as the actor where we are bound to * may be destroyed in this function. We need to keep alive this action until * function ends. */ g_object_ref(self); /* Unset styles */ if(priv->source && XFDASHBOARD_IS_ACTOR(priv->source)) { xfdashboard_stylable_remove_pseudo_class(XFDASHBOARD_STYLABLE(priv->source), "drag-source"); } if(XFDASHBOARD_IS_ACTOR(priv->actor)) { xfdashboard_stylable_remove_pseudo_class(XFDASHBOARD_STYLABLE(priv->actor), "dragged"); } if(priv->dragHandle && XFDASHBOARD_IS_ACTOR(priv->dragHandle)) { gchar *styleClass; if(priv->source) { styleClass=g_strdup_printf("drag-source-%s", G_OBJECT_TYPE_NAME(priv->source)); xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(priv->dragHandle), styleClass); g_free(styleClass); } styleClass=g_strdup_printf("drag-actor-%s", G_OBJECT_TYPE_NAME(priv->actor)); xfdashboard_stylable_remove_class(XFDASHBOARD_STYLABLE(priv->dragHandle), styleClass); g_free(styleClass); xfdashboard_stylable_remove_pseudo_class(XFDASHBOARD_STYLABLE(priv->dragHandle), "drag-handle"); priv->dragHandle=NULL; } if(priv->dragHandleChangedID) { g_signal_handler_disconnect(self, priv->dragHandleChangedID); priv->dragHandleChangedID=0; } /* Remove 'destroy' signal on dragged actor */ if(priv->actorDestroySignalID) { g_signal_handler_disconnect(priv->actor, priv->actorDestroySignalID); priv->actorDestroySignalID=0; } /* Remove our listerners for allocation changes */ for(list=priv->targets; list; list=g_slist_next(list)) { XfdashboardDropAction *target=XFDASHBOARD_DROP_ACTION(list->data); ClutterActorMeta *actorMeta=CLUTTER_ACTOR_META(target); ClutterActor *actor=clutter_actor_meta_get_actor(actorMeta); g_signal_handlers_disconnect_by_func(actor, G_CALLBACK(_xfdashboard_drag_on_drop_target_allocation_changed), self); } /* Find drop target at stage coordinate if dragged actor was not destroyed */ if(!priv->dragCancelled) dropTarget=_xfdashboard_drag_action_find_drop_traget_at_coord(self, inStageX, inStageY); /* If drop target was found check if we are allowed to drop on it. */ if(dropTarget) { /* Transform event coordinates relative to drop target */ _xfdashboard_drag_action_transform_stage_point(dropTarget, inStageX, inStageY, &dropX, &dropY); /* Ask drop target if we are allowed to drop dragged actor on it */ g_signal_emit_by_name(dropTarget, "can-drop", self, dropX, dropY, &canDrop); } /* If we cannot drop the draggged actor emit "drag-cancel" on dragged actor */ if(!canDrop) g_signal_emit_by_name(inAction, "drag-cancel", priv->actor, inStageX, inStageY, NULL); /* Iterate through list of drop targets to emit the "end" signal to the ones * on which the dragged actor will not be drop (either they were not the target * or it did not allow to drop on it). The real drop target gets the "drop" * signal. */ for(list=priv->targets; list; list=g_slist_next(list)) { XfdashboardDropAction *target=XFDASHBOARD_DROP_ACTION(list->data); if(canDrop && target && target==dropTarget) { g_signal_emit_by_name(dropTarget, "drop", self, dropX, dropY, NULL); } else { g_signal_emit_by_name(target, "end", self, NULL); } } /* Call parent's class method at last */ if(dragActionClass->drag_end) dragActionClass->drag_end(inAction, inActor, inStageX, inStageY, inModifiers); /* Forget dragged actor as dragging has ended now */ priv->actor=NULL; /* Free list of drop targets and unref each drop target */ if(priv->targets) g_slist_free_full(priv->targets, g_object_unref); priv->targets=NULL; /* Free list of actor we crossed by motion */ if(priv->lastMotionActors) { g_slist_foreach(priv->lastMotionActors, _xfdashboard_drag_action_on_motion_actor_destroyed, self); if(priv->lastMotionActors) g_slist_free(priv->lastMotionActors); priv->lastMotionActors=NULL; } /* Reset variables */ priv->lastDropTarget=NULL; /* Release the reference we took at function beginning */ g_object_unref(self); }
/* Dragging of actor begins */ static void _xfdashboard_drag_action_drag_begin(ClutterDragAction *inAction, ClutterActor *inActor, gfloat inStageX, gfloat inStageY, ClutterModifierType inModifiers) { XfdashboardDragAction *self; XfdashboardDragActionPrivate *priv; ClutterDragActionClass *dragActionClass; GSList *list; g_return_if_fail(XFDASHBOARD_IS_DRAG_ACTION(inAction)); self=XFDASHBOARD_DRAG_ACTION(inAction); priv=self->priv; dragActionClass=CLUTTER_DRAG_ACTION_CLASS(xfdashboard_drag_action_parent_class); /* Call parent's class method */ if(dragActionClass->drag_begin) dragActionClass->drag_begin(inAction, inActor, inStageX, inStageY, inModifiers); /* Remember dragged actor while dragging and listen to possible 'destroy' signal emissions */ priv->actor=inActor; priv->actorDestroySignalID=g_signal_connect_swapped(priv->actor, "destroy", G_CALLBACK(_xfdashboard_drag_action_on_dragged_actor_destroyed), self); /* Get list of drop targets. It is a new list with all current * drop targets already reffed. So the drop targets will be valid * while dragging */ priv->targets=xfdashboard_drop_action_get_targets(); /* Emit "begin" signal on all drop targets to determine if they * can handle dragged actor and to prepare them for dragging. * All targets returning TRUE (and therefore telling us they * can handle dragged actor and are prepared for drag'n'drop) * will be sorted. */ list=priv->targets; while(list) { XfdashboardDropAction *dropTarget=XFDASHBOARD_DROP_ACTION(list->data); gboolean canHandle=FALSE; g_signal_emit_by_name(dropTarget, "begin", self, &canHandle); if(!canHandle) { GSList *next; /* Drop target cannot handle dragged actor so unref it and * remove it from list of drop targets */ next=g_slist_next(list); priv->targets=g_slist_remove_link(priv->targets, list); g_object_unref(list->data); g_slist_free_1(list); list=next; } else list=g_slist_next(list); } _xfdashboard_drag_action_sort_targets(self); /* We should listen to allocation changes for each actor which * is an active drop target. */ for(list=priv->targets; list; list=g_slist_next(list)) { XfdashboardDropAction *dropTarget=XFDASHBOARD_DROP_ACTION(list->data); ClutterActorMeta *actorMeta=CLUTTER_ACTOR_META(dropTarget); ClutterActor *actor=clutter_actor_meta_get_actor(actorMeta); g_signal_connect_swapped(actor, "allocation-changed", G_CALLBACK(_xfdashboard_drag_on_drop_target_allocation_changed), self); } /* Setup for dragging */ priv->dragCancelled=FALSE; priv->lastDropTarget=NULL; priv->lastMotionActors=NULL; /* Set styles */ if(priv->source && XFDASHBOARD_IS_ACTOR(priv->source)) { xfdashboard_stylable_add_pseudo_class(XFDASHBOARD_STYLABLE(priv->source), "drag-source"); } if(XFDASHBOARD_IS_ACTOR(priv->actor)) { xfdashboard_stylable_add_pseudo_class(XFDASHBOARD_STYLABLE(priv->actor), "dragged"); } priv->dragHandle=clutter_drag_action_get_drag_handle(CLUTTER_DRAG_ACTION(self)); if(priv->dragHandle && XFDASHBOARD_IS_ACTOR(priv->dragHandle)) { gchar *styleClass; if(priv->source) { styleClass=g_strdup_printf("drag-source-%s", G_OBJECT_TYPE_NAME(priv->source)); xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->dragHandle), styleClass); g_free(styleClass); } styleClass=g_strdup_printf("drag-actor-%s", G_OBJECT_TYPE_NAME(priv->actor)); xfdashboard_stylable_add_class(XFDASHBOARD_STYLABLE(priv->dragHandle), styleClass); g_free(styleClass); xfdashboard_stylable_add_pseudo_class(XFDASHBOARD_STYLABLE(priv->dragHandle), "drag-handle"); /* Get notified if drag handle changes */ priv->dragHandleChangedID=g_signal_connect(self, "notify::drag-handle", G_CALLBACK(_xfdashboard_drag_action_on_drag_handle_changed), NULL); } }