cleanup specfile for packaging
[profile/ivi/clutter.git] / clutter / clutter-drag-action.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author:
22  *   Emmanuele Bassi <ebassi@linux.intel.com>
23  */
24
25 /**
26  * SECTION:clutter-drag-action
27  * @Title: ClutterDragAction
28  * @Short_Description: Action enabling dragging on actors
29  *
30  * #ClutterDragAction is a sub-class of #ClutterAction that implements
31  * all the necessary logic for dragging actors.
32  *
33  * The simplest usage of #ClutterDragAction consists in adding it to
34  * a #ClutterActor and setting it as reactive; for instance, the following
35  * code:
36  *
37  * |[
38  *   clutter_actor_add_action (actor, clutter_drag_action_new ());
39  *   clutter_actor_set_reactive (actor, TRUE);
40  * ]|
41  *
42  * will automatically result in the actor moving to follow the pointer
43  * whenever the pointer's button is pressed over the actor and moved
44  * across the stage.
45  *
46  * The #ClutterDragAction will signal the begin and the end of a dragging
47  * through the #ClutterDragAction::drag-begin and #ClutterDragAction::drag-end
48  * signals, respectively. Each pointer motion during a drag will also result
49  * in the #ClutterDragAction::drag-motion signal to be emitted.
50  *
51  * It is also possible to set another #ClutterActor as the dragged actor
52  * by calling clutter_drag_action_set_drag_handle() from within a handle
53  * of the #ClutterDragAction::drag-begin signal. The drag handle must be
54  * parented and exist between the emission of #ClutterDragAction::drag-begin
55  * and #ClutterDragAction::drag-end.
56  *
57  * <example id="drag-action-example">
58  *   <title>A simple draggable actor</title>
59  *   <programlisting>
60  * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-drag.c">
61  *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
62  * </xi:include>
63  *   </programlisting>
64  *   <para>The example program above allows dragging the rectangle around
65  *   the stage using a #ClutterDragAction. When pressing the
66  *   <keycap>Shift</keycap> key the actor that is going to be dragged is a
67  *   separate rectangle, and when the drag ends, the original rectangle will
68  *   be animated to the final coordinates.</para>
69  * </example>
70  *
71  * #ClutterDragAction is available since Clutter 1.4
72  */
73
74 #ifdef HAVE_CONFIG_H
75 #include "config.h"
76 #endif
77
78 #include "clutter-drag-action.h"
79
80 #include "clutter-debug.h"
81 #include "clutter-enum-types.h"
82 #include "clutter-marshal.h"
83 #include "clutter-private.h"
84 #include "clutter-stage-private.h"
85
86 struct _ClutterDragActionPrivate
87 {
88   ClutterStage *stage;
89
90   gint x_drag_threshold;
91   gint y_drag_threshold;
92   ClutterActor *drag_handle;
93   ClutterDragAxis drag_axis;
94
95   ClutterInputDevice *device;
96   gulong button_press_id;
97   gulong capture_id;
98
99   gfloat press_x;
100   gfloat press_y;
101   ClutterModifierType press_state;
102
103   gfloat last_motion_x;
104   gfloat last_motion_y;
105   ClutterModifierType last_motion_state;
106   ClutterInputDevice *last_motion_device;
107
108   gfloat transformed_press_x;
109   gfloat transformed_press_y;
110
111   guint emit_delayed_press    : 1;
112   guint in_drag               : 1;
113   guint motion_events_enabled : 1;
114 };
115
116 enum
117 {
118   PROP_0,
119
120   PROP_X_DRAG_THRESHOLD,
121   PROP_Y_DRAG_THRESHOLD,
122   PROP_DRAG_HANDLE,
123   PROP_DRAG_AXIS,
124
125   PROP_LAST
126 };
127
128 static GParamSpec *drag_props[PROP_LAST] = { NULL, };
129
130 enum
131 {
132   DRAG_BEGIN,
133   DRAG_MOTION,
134   DRAG_END,
135
136   LAST_SIGNAL
137 };
138
139 static guint drag_signals[LAST_SIGNAL] = { 0, };
140
141 /* forward declaration */
142 static gboolean on_captured_event (ClutterActor      *stage,
143                                    ClutterEvent      *event,
144                                    ClutterDragAction *action);
145
146 G_DEFINE_TYPE (ClutterDragAction, clutter_drag_action, CLUTTER_TYPE_ACTION);
147
148 static void
149 get_drag_threshold (ClutterDragAction *action,
150                     gint              *x_threshold,
151                     gint              *y_threshold)
152 {
153   ClutterDragActionPrivate *priv = action->priv;
154   ClutterSettings *settings = clutter_settings_get_default ();
155   gint x_res, y_res, default_threshold;
156
157   g_object_get (settings, "dnd-drag-threshold", &default_threshold, NULL);
158
159   if (priv->x_drag_threshold < 0)
160     x_res = default_threshold;
161   else
162     x_res = priv->x_drag_threshold;
163
164   if (priv->y_drag_threshold < 0)
165     y_res = default_threshold;
166   else
167     y_res = priv->y_drag_threshold;
168
169   if (x_threshold != NULL)
170     *x_threshold = x_res;
171
172   if (y_threshold != NULL)
173     *y_threshold = y_res;
174 }
175
176 static void
177 emit_drag_begin (ClutterDragAction *action,
178                  ClutterActor      *actor,
179                  ClutterEvent      *event)
180 {
181   ClutterDragActionPrivate *priv = action->priv;
182
183   if (priv->stage != NULL)
184     {
185       clutter_stage_set_motion_events_enabled (priv->stage, FALSE);
186       _clutter_stage_add_drag_actor (priv->stage,
187                                      clutter_event_get_device (event),
188                                      priv->drag_handle != NULL
189                                        ? priv->drag_handle
190                                        : actor);
191     }
192
193   g_signal_emit (action, drag_signals[DRAG_BEGIN], 0,
194                  actor,
195                  priv->press_x, priv->press_y,
196                  priv->press_state);
197 }
198
199 static void
200 emit_drag_motion (ClutterDragAction *action,
201                   ClutterActor      *actor,
202                   ClutterEvent      *event)
203 {
204   ClutterDragActionPrivate *priv = action->priv;
205   ClutterActor *drag_handle = NULL;
206   gfloat delta_x, delta_y;
207   gfloat motion_x, motion_y;
208
209   clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y);
210   priv->last_motion_state = clutter_event_get_state (event);
211   priv->last_motion_device = clutter_event_get_device (event);
212
213   if (priv->drag_handle != NULL && !priv->emit_delayed_press)
214     drag_handle = priv->drag_handle;
215   else
216     drag_handle = actor;
217
218   motion_x = motion_y = 0.0f;
219   clutter_actor_transform_stage_point (drag_handle,
220                                        priv->last_motion_x,
221                                        priv->last_motion_y,
222                                        &motion_x, &motion_y);
223
224   delta_x = delta_y = 0.0f;
225
226   switch (priv->drag_axis)
227     {
228     case CLUTTER_DRAG_AXIS_NONE:
229       delta_x = motion_x - priv->transformed_press_x;
230       delta_y = motion_y - priv->transformed_press_y;
231       break;
232
233     case CLUTTER_DRAG_X_AXIS:
234       delta_x = motion_x - priv->transformed_press_x;
235       break;
236
237     case CLUTTER_DRAG_Y_AXIS:
238       delta_y = motion_y - priv->transformed_press_y;
239       break;
240
241     default:
242       g_assert_not_reached ();
243       return;
244     }
245
246   if (priv->emit_delayed_press)
247     {
248       gint x_drag_threshold, y_drag_threshold;
249
250       get_drag_threshold (action, &x_drag_threshold, &y_drag_threshold);
251
252       if (ABS (delta_x) >= x_drag_threshold ||
253           ABS (delta_y) >= y_drag_threshold)
254         {
255           priv->emit_delayed_press = FALSE;
256
257           emit_drag_begin (action, actor, event);
258         }
259       else
260         return;
261     }
262
263   g_signal_emit (action, drag_signals[DRAG_MOTION], 0,
264                  actor,
265                  delta_x, delta_y);
266 }
267
268 static void
269 emit_drag_end (ClutterDragAction *action,
270                ClutterActor      *actor,
271                ClutterEvent      *event)
272 {
273   ClutterDragActionPrivate *priv = action->priv;
274
275   /* if we have an event, update our own state, otherwise we'll
276    * just use the currently stored state when emitting the ::drag-end
277    * signal
278    */
279   if (event != NULL)
280     {
281       clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y);
282       priv->last_motion_state = clutter_event_get_state (event);
283       priv->last_motion_device = clutter_event_get_device (event);
284     }
285
286   /* we might not have emitted ::drag-begin yet */
287   if (!priv->emit_delayed_press)
288     g_signal_emit (action, drag_signals[DRAG_END], 0,
289                    actor,
290                    priv->last_motion_x, priv->last_motion_y,
291                    priv->last_motion_state);
292
293   /* disconnect the capture */
294   if (priv->capture_id != 0)
295     {
296       g_signal_handler_disconnect (priv->stage, priv->capture_id);
297       priv->capture_id = 0;
298     }
299
300   clutter_stage_set_motion_events_enabled (priv->stage,
301                                            priv->motion_events_enabled);
302
303   if (priv->last_motion_device != NULL)
304     _clutter_stage_remove_drag_actor (priv->stage, priv->last_motion_device);
305
306   priv->in_drag = FALSE;
307 }
308
309 static gboolean
310 on_captured_event (ClutterActor      *stage,
311                    ClutterEvent      *event,
312                    ClutterDragAction *action)
313 {
314   ClutterDragActionPrivate *priv = action->priv;
315   ClutterActor *actor;
316   
317   actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action));
318
319   if (!priv->in_drag)
320     return CLUTTER_EVENT_PROPAGATE;
321
322   if (clutter_event_get_device (event) != priv->device)
323     return CLUTTER_EVENT_PROPAGATE;
324
325   switch (clutter_event_type (event))
326     {
327     case CLUTTER_MOTION:
328       {
329         ClutterModifierType mods = clutter_event_get_state (event);
330
331         /* we might miss a button-release event in case of grabs,
332          * so we need to check whether the button is still down
333          * during a motion event
334          */
335         if (mods & CLUTTER_BUTTON1_MASK)
336           emit_drag_motion (action, actor, event);
337         else
338           emit_drag_end (action, actor, event);
339       }
340       break;
341
342     case CLUTTER_BUTTON_RELEASE:
343       if (priv->in_drag)
344         emit_drag_end (action, actor, event);
345       break;
346
347     case CLUTTER_ENTER:
348     case CLUTTER_LEAVE:
349       if (priv->in_drag)
350         return CLUTTER_EVENT_STOP;
351       break;
352
353     default:
354       break;
355     }
356
357   return CLUTTER_EVENT_PROPAGATE;
358 }
359
360 static gboolean
361 on_button_press (ClutterActor      *actor,
362                  ClutterEvent      *event,
363                  ClutterDragAction *action)
364 {
365   ClutterDragActionPrivate *priv = action->priv;
366
367   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action)))
368     return CLUTTER_EVENT_PROPAGATE;
369
370   /* dragging is only performed using the primary button */
371   if (clutter_event_get_button (event) != CLUTTER_BUTTON_PRIMARY)
372     return CLUTTER_EVENT_PROPAGATE;
373
374   if (priv->stage == NULL)
375     priv->stage = CLUTTER_STAGE (clutter_actor_get_stage (actor));
376
377   clutter_event_get_coords (event, &priv->press_x, &priv->press_y);
378   priv->press_state = clutter_event_get_state (event);
379
380   priv->device = clutter_event_get_device (event);
381
382   priv->last_motion_x = priv->press_x;
383   priv->last_motion_y = priv->press_y;
384
385   priv->transformed_press_x = priv->press_x;
386   priv->transformed_press_y = priv->press_y;
387   clutter_actor_transform_stage_point (actor, priv->press_x, priv->press_y,
388                                        &priv->transformed_press_x,
389                                        &priv->transformed_press_y);
390
391   priv->motion_events_enabled =
392     clutter_stage_get_motion_events_enabled (priv->stage);
393
394   if (priv->x_drag_threshold == 0 || priv->y_drag_threshold == 0)
395     emit_drag_begin (action, actor, event);
396   else
397     priv->emit_delayed_press = TRUE;
398
399   priv->in_drag = TRUE;
400   priv->capture_id = g_signal_connect_after (priv->stage, "captured-event",
401                                              G_CALLBACK (on_captured_event),
402                                              action);
403
404   return CLUTTER_EVENT_PROPAGATE;
405 }
406
407 static void
408 clutter_drag_action_set_actor (ClutterActorMeta *meta,
409                                ClutterActor     *actor)
410 {
411   ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (meta)->priv;
412
413   if (priv->button_press_id != 0)
414     {
415       ClutterActor *old_actor;
416
417       old_actor = clutter_actor_meta_get_actor (meta);
418       if (old_actor != NULL)
419         g_signal_handler_disconnect (old_actor, priv->button_press_id);
420         
421       priv->button_press_id = 0;
422     }
423
424   if (priv->capture_id != 0)
425     {
426       if (priv->stage != NULL)
427         g_signal_handler_disconnect (priv->stage, priv->capture_id);
428
429       priv->capture_id = 0;
430       priv->stage = NULL;
431     }
432
433   clutter_drag_action_set_drag_handle (CLUTTER_DRAG_ACTION (meta), NULL);
434
435   priv->in_drag = FALSE;
436
437   if (actor != NULL)
438     priv->button_press_id = g_signal_connect (actor, "button-press-event",
439                                               G_CALLBACK (on_button_press),
440                                               meta);
441
442   CLUTTER_ACTOR_META_CLASS (clutter_drag_action_parent_class)->set_actor (meta, actor);
443 }
444
445 static void
446 clutter_drag_action_real_drag_motion (ClutterDragAction *action,
447                                       ClutterActor      *actor,
448                                       gfloat             delta_x,
449                                       gfloat             delta_y)
450 {
451   ClutterActor *drag_handle;
452
453   if (action->priv->drag_handle != NULL)
454     drag_handle = action->priv->drag_handle;
455   else
456     drag_handle = actor;
457
458   clutter_actor_move_by (drag_handle, delta_x, delta_y);
459 }
460
461 static void
462 clutter_drag_action_set_property (GObject      *gobject,
463                                   guint         prop_id,
464                                   const GValue *value,
465                                   GParamSpec   *pspec)
466 {
467   ClutterDragAction *action = CLUTTER_DRAG_ACTION (gobject);
468   ClutterDragActionPrivate *priv = action->priv;
469
470   switch (prop_id)
471     {
472     case PROP_X_DRAG_THRESHOLD:
473       clutter_drag_action_set_drag_threshold (action,
474                                               g_value_get_int (value),
475                                               priv->y_drag_threshold);
476       break;
477
478     case PROP_Y_DRAG_THRESHOLD:
479       clutter_drag_action_set_drag_threshold (action,
480                                               priv->x_drag_threshold,
481                                               g_value_get_int (value));
482       break;
483
484     case PROP_DRAG_HANDLE:
485       clutter_drag_action_set_drag_handle (action, g_value_get_object (value));
486       break;
487
488     case PROP_DRAG_AXIS:
489       clutter_drag_action_set_drag_axis (action, g_value_get_enum (value));
490       break;
491
492     default:
493       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
494     }
495 }
496
497 static void
498 clutter_drag_action_get_property (GObject    *gobject,
499                                   guint       prop_id,
500                                   GValue     *value,
501                                   GParamSpec *pspec)
502 {
503   ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv;
504
505   switch (prop_id)
506     {
507     case PROP_X_DRAG_THRESHOLD:
508       {
509         gint threshold;
510
511         get_drag_threshold (CLUTTER_DRAG_ACTION (gobject), &threshold, NULL);
512         g_value_set_int (value, threshold);
513       }
514       break;
515
516     case PROP_Y_DRAG_THRESHOLD:
517       {
518         gint threshold;
519
520         get_drag_threshold (CLUTTER_DRAG_ACTION (gobject), NULL, &threshold);
521         g_value_set_int (value, threshold);
522       }
523       break;
524
525     case PROP_DRAG_HANDLE:
526       g_value_set_object (value, priv->drag_handle);
527       break;
528
529     case PROP_DRAG_AXIS:
530       g_value_set_enum (value, priv->drag_axis);
531       break;
532
533     default:
534       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
535     }
536 }
537
538 static void
539 clutter_drag_action_dispose (GObject *gobject)
540 {
541   ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv;
542
543   if (priv->capture_id != 0)
544     {
545       if (priv->stage != NULL)
546         g_signal_handler_disconnect (priv->stage, priv->capture_id);
547
548       priv->capture_id = 0;
549       priv->stage = NULL;
550     }
551
552   if (priv->button_press_id != 0)
553     {
554       ClutterActor *actor;
555
556       actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (gobject));
557       if (actor != NULL)
558         g_signal_handler_disconnect (actor, priv->button_press_id);
559
560       priv->button_press_id = 0;
561     }
562
563   clutter_drag_action_set_drag_handle (CLUTTER_DRAG_ACTION (gobject), NULL);
564
565   G_OBJECT_CLASS (clutter_drag_action_parent_class)->dispose (gobject);
566 }
567
568 static void
569 clutter_drag_action_class_init (ClutterDragActionClass *klass)
570 {
571   ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass);
572   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
573
574   g_type_class_add_private (klass, sizeof (ClutterDragActionPrivate));
575
576   meta_class->set_actor = clutter_drag_action_set_actor;
577
578   klass->drag_motion = clutter_drag_action_real_drag_motion;
579
580   /**
581    * ClutterDragAction:x-drag-threshold:
582    *
583    * The horizontal threshold, in pixels, that the cursor must travel
584    * in order to begin a drag action.
585    *
586    * When set to a positive value, #ClutterDragAction will only emit
587    * #ClutterDragAction::drag-begin if the pointer has moved
588    * horizontally at least of the given amount of pixels since
589    * the button press event.
590    *
591    * When set to -1, #ClutterDragAction will use the default threshold
592    * stored in the #ClutterSettings:dnd-drag-threshold property of
593    * #ClutterSettings.
594    *
595    * When read, this property will always return a valid drag
596    * threshold, either as set or the default one.
597    *
598    * Since: 1.4
599    */
600   drag_props[PROP_X_DRAG_THRESHOLD] =
601     g_param_spec_int ("x-drag-threshold",
602                       P_("Horizontal Drag Threshold"),
603                       P_("The horizontal amount of pixels required to start dragging"),
604                       -1, G_MAXINT,
605                       0,
606                       CLUTTER_PARAM_READWRITE);
607
608   /**
609    * ClutterDragAction:y-drag-threshold:
610    *
611    * The vertical threshold, in pixels, that the cursor must travel
612    * in order to begin a drag action.
613    *
614    * When set to a positive value, #ClutterDragAction will only emit
615    * #ClutterDragAction::drag-begin if the pointer has moved
616    * vertically at least of the given amount of pixels since
617    * the button press event.
618    *
619    * When set to -1, #ClutterDragAction will use the value stored
620    * in the #ClutterSettings:dnd-drag-threshold property of
621    * #ClutterSettings.
622    *
623    * When read, this property will always return a valid drag
624    * threshold, either as set or the default one.
625    *
626    * Since: 1.4
627    */
628   drag_props[PROP_Y_DRAG_THRESHOLD] =
629     g_param_spec_int ("y-drag-threshold",
630                       P_("Vertical Drag Threshold"),
631                       P_("The vertical amount of pixels required to start dragging"),
632                       -1, G_MAXINT,
633                       0,
634                       CLUTTER_PARAM_READWRITE);
635
636   /**
637    * ClutterDragAction:drag-handle:
638    *
639    * The #ClutterActor that is effectively being dragged
640    *
641    * A #ClutterDragAction will, be default, use the #ClutterActor that
642    * has been attached to the action; it is possible to create a
643    * separate #ClutterActor and use it instead.
644    *
645    * Setting this property has no effect on the #ClutterActor argument
646    * passed to the #ClutterDragAction signals
647    *
648    * Since: 1.4
649    */
650   drag_props[PROP_DRAG_HANDLE] =
651     g_param_spec_object ("drag-handle",
652                          P_("Drag Handle"),
653                          P_("The actor that is being dragged"),
654                          CLUTTER_TYPE_ACTOR,
655                          CLUTTER_PARAM_READWRITE);
656
657   /**
658    * ClutterDragAction:drag-axis:
659    *
660    * Constraints the dragging action to the specified axis
661    *
662    * Since: 1.4
663    */
664   drag_props[PROP_DRAG_AXIS] =
665     g_param_spec_enum ("drag-axis",
666                        P_("Drag Axis"),
667                        P_("Constraints the dragging to an axis"),
668                        CLUTTER_TYPE_DRAG_AXIS,
669                        CLUTTER_DRAG_AXIS_NONE,
670                        CLUTTER_PARAM_READWRITE);
671
672   gobject_class->set_property = clutter_drag_action_set_property;
673   gobject_class->get_property = clutter_drag_action_get_property;
674   gobject_class->dispose = clutter_drag_action_dispose;
675   g_object_class_install_properties  (gobject_class,
676                                       PROP_LAST,
677                                       drag_props);
678
679   /**
680    * ClutterDragAction::drag-begin:
681    * @action: the #ClutterDragAction that emitted the signal
682    * @actor: the #ClutterActor attached to the action
683    * @event_x: the X coordinate (in stage space) of the press event
684    * @event_y: the Y coordinate (in stage space) of the press event
685    * @modifiers: the modifiers of the press event
686    *
687    * The ::drag-begin signal is emitted when the #ClutterDragAction
688    * starts the dragging
689    *
690    * The emission of this signal can be delayed by using the
691    * #ClutterDragAction:x-drag-threshold and
692    * #ClutterDragAction:y-drag-threshold properties
693    *
694    * Since: 1.4
695    */
696   drag_signals[DRAG_BEGIN] =
697     g_signal_new (I_("drag-begin"),
698                   CLUTTER_TYPE_DRAG_ACTION,
699                   G_SIGNAL_RUN_LAST,
700                   G_STRUCT_OFFSET (ClutterDragActionClass, drag_begin),
701                   NULL, NULL,
702                   _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS,
703                   G_TYPE_NONE, 4,
704                   CLUTTER_TYPE_ACTOR,
705                   G_TYPE_FLOAT,
706                   G_TYPE_FLOAT,
707                   CLUTTER_TYPE_MODIFIER_TYPE);
708
709   /**
710    * ClutterDragAction::drag-motion
711    * @action: the #ClutterDragAction that emitted the signal
712    * @actor: the #ClutterActor attached to the action
713    * @delta_x: the X component of the distance between the press event
714    *   that began the dragging and the current position of the pointer,
715    *   as of the latest motion event
716    * @delta_y: the Y component of the distance between the press event
717    *   that began the dragging and the current position of the pointer,
718    *   as of the latest motion event
719    *
720    * The ::drag-motion signal is emitted for each motion event after
721    * the #ClutterDragAction::drag-begin signal has been emitted.
722    *
723    * The components of the distance between the press event and the
724    * latest motion event are computed in the actor's coordinate space,
725    * to take into account eventual transformations. If you want the
726    * stage coordinates of the latest motion event you can use
727    * clutter_drag_action_get_motion_coords().
728    *
729    * The default handler of the signal will call clutter_actor_move_by()
730    * either on @actor or, if set, of #ClutterDragAction:drag-handle using
731    * the @delta_x and @delta_y components of the dragging motion. If you
732    * want to override the default behaviour, you can connect to this
733    * signal and call g_signal_stop_emission_by_name() from within your
734    * callback.
735    *
736    * Since: 1.4
737    */
738   drag_signals[DRAG_MOTION] =
739     g_signal_new (I_("drag-motion"),
740                   CLUTTER_TYPE_DRAG_ACTION,
741                   G_SIGNAL_RUN_LAST,
742                   G_STRUCT_OFFSET (ClutterDragActionClass, drag_motion),
743                   NULL, NULL,
744                   _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT,
745                   G_TYPE_NONE, 3,
746                   CLUTTER_TYPE_ACTOR,
747                   G_TYPE_FLOAT,
748                   G_TYPE_FLOAT);
749
750   /**
751    * ClutterDragAction::drag-end:
752    * @action: the #ClutterDragAction that emitted the signal
753    * @actor: the #ClutterActor attached to the action
754    * @event_x: the X coordinate (in stage space) of the release event
755    * @event_y: the Y coordinate (in stage space) of the release event
756    * @modifiers: the modifiers of the release event
757    *
758    * The ::drag-end signal is emitted at the end of the dragging,
759    * when the pointer button's is released
760    *
761    * This signal is emitted if and only if the #ClutterDragAction::drag-begin
762    * signal has been emitted first
763    *
764    * Since: 1.4
765    */
766   drag_signals[DRAG_END] =
767     g_signal_new (I_("drag-end"),
768                   CLUTTER_TYPE_DRAG_ACTION,
769                   G_SIGNAL_RUN_LAST,
770                   G_STRUCT_OFFSET (ClutterDragActionClass, drag_end),
771                   NULL, NULL,
772                   _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS,
773                   G_TYPE_NONE, 4,
774                   CLUTTER_TYPE_ACTOR,
775                   G_TYPE_FLOAT,
776                   G_TYPE_FLOAT,
777                   CLUTTER_TYPE_MODIFIER_TYPE);
778 }
779
780 static void
781 clutter_drag_action_init (ClutterDragAction *self)
782 {
783   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_DRAG_ACTION,
784                                             ClutterDragActionPrivate);
785 }
786
787 /**
788  * clutter_drag_action_new:
789  *
790  * Creates a new #ClutterDragAction instance
791  *
792  * Return value: the newly created #ClutterDragAction
793  *
794  * Since: 1.4
795  */
796 ClutterAction *
797 clutter_drag_action_new (void)
798 {
799   return g_object_new (CLUTTER_TYPE_DRAG_ACTION, NULL);
800 }
801
802 /**
803  * clutter_drag_action_set_drag_threshold:
804  * @action: a #ClutterDragAction
805  * @x_threshold: a distance on the horizontal axis, in pixels, or
806  *   -1 to use the default drag threshold from #ClutterSettings
807  * @y_threshold: a distance on the vertical axis, in pixels, or
808  *   -1 to use the default drag threshold from #ClutterSettings
809  *
810  * Sets the horizontal and vertical drag thresholds that must be
811  * cleared by the pointer before @action can begin the dragging.
812  *
813  * If @x_threshold or @y_threshold are set to -1 then the default
814  * drag threshold stored in the #ClutterSettings:dnd-drag-threshold
815  * property of #ClutterSettings will be used.
816  *
817  * Since: 1.4
818  */
819 void
820 clutter_drag_action_set_drag_threshold (ClutterDragAction *action,
821                                         gint               x_threshold,
822                                         gint               y_threshold)
823 {
824   ClutterDragActionPrivate *priv;
825   GObject *self;
826
827   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
828
829   self = G_OBJECT (action);
830   priv = action->priv;
831
832   g_object_freeze_notify (self);
833
834   if (priv->x_drag_threshold != x_threshold)
835     {
836       priv->x_drag_threshold = x_threshold;
837
838       g_object_notify_by_pspec (self, drag_props[PROP_X_DRAG_THRESHOLD]);
839     }
840
841   if (priv->y_drag_threshold != y_threshold)
842     {
843       priv->y_drag_threshold = y_threshold;
844
845       g_object_notify_by_pspec (self, drag_props[PROP_Y_DRAG_THRESHOLD]);
846     }
847
848   g_object_thaw_notify (self);
849 }
850
851 /**
852  * clutter_drag_action_get_drag_threshold:
853  * @action: a #ClutterDragAction
854  * @x_threshold: (out): return location for the horizontal drag
855  *   threshold value, in pixels
856  * @y_threshold: (out): return location for the vertical drag
857  *   threshold value, in pixels
858  *
859  * Retrieves the values set by clutter_drag_action_set_drag_threshold().
860  *
861  * If the #ClutterDragAction:x-drag-threshold property or the
862  * #ClutterDragAction:y-drag-threshold property have been set to -1 then
863  * this function will return the default drag threshold value as stored
864  * by the #ClutterSettings:dnd-drag-threshold property of #ClutterSettings.
865  *
866  * Since: 1.4
867  */
868 void
869 clutter_drag_action_get_drag_threshold (ClutterDragAction *action,
870                                         guint             *x_threshold,
871                                         guint             *y_threshold)
872 {
873   gint x_res, y_res;
874
875   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
876
877   get_drag_threshold (action, &x_res, &y_res);
878
879   if (x_threshold != NULL)
880     *x_threshold = x_res;
881
882   if (y_threshold != NULL)
883     *y_threshold = y_res;
884 }
885
886 static void
887 on_drag_handle_destroy (ClutterActor      *actor,
888                         ClutterDragAction *action)
889 {
890   ClutterDragActionPrivate *priv = action->priv;
891
892   /* make sure we reset the state */
893   if (priv->in_drag)
894     emit_drag_end (action, actor, NULL);
895
896   priv->drag_handle = NULL;
897 }
898
899 /**
900  * clutter_drag_action_set_drag_handle:
901  * @action: a #ClutterDragAction
902  * @handle: (allow-none): a #ClutterActor, or %NULL to unset
903  *
904  * Sets the actor to be used as the drag handle.
905  *
906  * Since: 1.4
907  */
908 void
909 clutter_drag_action_set_drag_handle (ClutterDragAction *action,
910                                      ClutterActor      *handle)
911 {
912   ClutterDragActionPrivate *priv;
913
914   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
915   g_return_if_fail (handle == NULL || CLUTTER_IS_ACTOR (handle));
916
917   priv = action->priv;
918
919   if (priv->drag_handle == handle)
920     return;
921
922   if (priv->drag_handle != NULL)
923     g_signal_handlers_disconnect_by_func (priv->drag_handle,
924                                           G_CALLBACK (on_drag_handle_destroy),
925                                           action);
926
927   priv->drag_handle = handle;
928
929   if (priv->drag_handle != NULL)
930     g_signal_connect (priv->drag_handle, "destroy",
931                       G_CALLBACK (on_drag_handle_destroy),
932                       action);
933
934   g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_HANDLE]);
935 }
936
937 /**
938  * clutter_drag_action_get_drag_handle:
939  * @action: a #ClutterDragAction
940  *
941  * Retrieves the drag handle set by clutter_drag_action_set_drag_handle()
942  *
943  * Return value: (transfer none): a #ClutterActor, used as the drag
944  *   handle, or %NULL if none was set
945  *
946  * Since: 1.4
947  */
948 ClutterActor *
949 clutter_drag_action_get_drag_handle (ClutterDragAction *action)
950 {
951   g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action), NULL);
952
953   return action->priv->drag_handle;
954 }
955
956 /**
957  * clutter_drag_action_set_drag_axis:
958  * @action: a #ClutterDragAction
959  * @axis: the axis to constraint the dragging to
960  *
961  * Restricts the dragging action to a specific axis
962  *
963  * Since: 1.4
964  */
965 void
966 clutter_drag_action_set_drag_axis (ClutterDragAction *action,
967                                    ClutterDragAxis    axis)
968 {
969   ClutterDragActionPrivate *priv;
970
971   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
972   g_return_if_fail (axis >= CLUTTER_DRAG_AXIS_NONE &&
973                     axis <= CLUTTER_DRAG_Y_AXIS);
974
975   priv = action->priv;
976
977   if (priv->drag_axis == axis)
978     return;
979
980   priv->drag_axis = axis;
981
982   g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_AXIS]);
983 }
984
985 /**
986  * clutter_drag_action_get_drag_axis:
987  * @action: a #ClutterDragAction
988  *
989  * Retrieves the axis constraint set by clutter_drag_action_set_drag_axis()
990  *
991  * Return value: the axis constraint
992  *
993  * Since: 1.4
994  */
995 ClutterDragAxis
996 clutter_drag_action_get_drag_axis (ClutterDragAction *action)
997 {
998   g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action),
999                         CLUTTER_DRAG_AXIS_NONE);
1000
1001   return action->priv->drag_axis;
1002 }
1003
1004 /**
1005  * clutter_drag_action_get_press_coords:
1006  * @action: a #ClutterDragAction
1007  * @press_x: (out): return location for the press event's X coordinate
1008  * @press_y: (out): return location for the press event's Y coordinate
1009  *
1010  * Retrieves the coordinates, in stage space, of the press event
1011  * that started the dragging
1012  *
1013  * Since: 1.4
1014  */
1015 void
1016 clutter_drag_action_get_press_coords (ClutterDragAction *action,
1017                                       gfloat            *press_x,
1018                                       gfloat            *press_y)
1019 {
1020   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
1021
1022   if (press_x)
1023     *press_x = action->priv->press_x;
1024
1025   if (press_y)
1026     *press_y = action->priv->press_y;
1027 }
1028
1029 /**
1030  * clutter_drag_action_get_motion_coords:
1031  * @action: a #ClutterDragAction
1032  * @motion_x: (out): return location for the latest motion
1033  *   event's X coordinate
1034  * @motion_y: (out): return location for the latest motion
1035  *   event's Y coordinate
1036  *
1037  * Retrieves the coordinates, in stage space, of the latest motion
1038  * event during the dragging
1039  *
1040  * Since: 1.4
1041  */
1042 void
1043 clutter_drag_action_get_motion_coords (ClutterDragAction *action,
1044                                        gfloat            *motion_x,
1045                                        gfloat            *motion_y)
1046 {
1047   g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action));
1048
1049   if (motion_x)
1050     *motion_x = action->priv->last_motion_x;
1051
1052   if (motion_y)
1053     *motion_y = action->priv->last_motion_y;
1054 }