/* Query if long-press events should be handled and signals emitted */ static void _xfdashboard_click_action_query_long_press(XfdashboardClickAction *self) { XfdashboardClickActionPrivate *priv; ClutterActor *actor; gboolean result; gint timeout; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); priv=self->priv; result=FALSE; /* If no duration was set get default one from settings */ if(priv->longPressDuration<0) { ClutterSettings *settings=clutter_settings_get_default(); g_object_get(settings, "long-press-duration", &timeout, NULL); } else timeout=priv->longPressDuration; /* Emit signal to determine if long-press should be supported */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(self)); g_signal_emit(self, XfdashboardClickActionSignals[SIGNAL_LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_QUERY, &result); if(result) { priv->longPressID=clutter_threads_add_timeout(timeout, _xfdashboard_click_action_emit_long_press, self); } }
/** * xfdashboard_click_action_get_coords: * @self: A #XfdashboardClickAction * @outPressX: (out): Return location for the X coordinate or %NULL * @outPressY: (out): Return location for the Y coordinate or %NULL * * Retrieves the screen coordinates of the button press. */ void xfdashboard_click_action_get_coords(XfdashboardClickAction *self, gfloat *outPressX, gfloat *outPressY) { g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); if(outPressX!=NULL) *outPressX=self->priv->pressX; if(outPressY!=NULL) *outPressY=self->priv->pressY; }
/* Emit "long-press" signal */ static gboolean _xfdashboard_click_action_emit_long_press(gpointer inUserData) { XfdashboardClickAction *self; XfdashboardClickActionPrivate *priv; ClutterActor *actor; gboolean result; g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inUserData), FALSE); self=XFDASHBOARD_CLICK_ACTION(inUserData); priv=self->priv; /* Reset variables */ priv->longPressID=0; /* Emit signal */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(inUserData)); g_signal_emit(self, XfdashboardClickActionSignals[SIGNAL_LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_ACTIVATE, &result); /* Disconnect signal handlers */ if(priv->captureID!=0) { g_signal_handler_disconnect(priv->stage, priv->captureID); priv->captureID=0; } /* Reset state of this action */ _xfdashboard_click_action_set_pressed(self, FALSE); _xfdashboard_click_action_set_held(self, FALSE); /* Event handled */ return(FALSE); }
/* Set press state */ static void _xfdashboard_click_action_set_pressed(XfdashboardClickAction *self, gboolean isPressed) { XfdashboardClickActionPrivate *priv; ClutterActor *actor; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); priv=self->priv; /* Set value if changed */ isPressed=!!isPressed; if(priv->isPressed!=isPressed) { /* Set value */ priv->isPressed=isPressed; /* Style state */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(self)); if(XFDASHBOARD_IS_ACTOR(actor)) { if(priv->isPressed) xfdashboard_stylable_add_pseudo_class(XFDASHBOARD_STYLABLE(actor), "pressed"); else xfdashboard_stylable_remove_pseudo_class(XFDASHBOARD_STYLABLE(actor), "pressed"); } /* Notify about property change */ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardClickActionProperties[PROP_PRESSED]); } }
/* A menu item was clicked */ static void _xfdashboard_popup_menu_item_meta_clicked(XfdashboardPopupMenuItemMeta *self, ClutterActor *inActor, gpointer inUserData) { g_return_if_fail(XFDASHBOARD_IS_POPUP_MENU_ITEM_META(self)); g_return_if_fail(CLUTTER_IS_ACTOR(inActor)); g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inUserData)); /* Activate menu item */ xfdashboard_popup_menu_item_meta_activate(self); }
/* Called when attaching and detaching a ClutterActorMeta instance to a ClutterActor */ static void _xfdashboard_click_action_set_actor(ClutterActorMeta *inActorMeta, ClutterActor *inActor) { XfdashboardClickAction *self; XfdashboardClickActionPrivate *priv; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inActorMeta)); self=XFDASHBOARD_CLICK_ACTION(inActorMeta); priv=self->priv; /* Disconnect signals and remove sources */ if(priv->eventID!=0) { ClutterActor *oldActor=clutter_actor_meta_get_actor(inActorMeta); if(oldActor!=NULL) g_signal_handler_disconnect(oldActor, priv->eventID); priv->eventID=0; } if(priv->captureID!=0) { if(priv->stage!=NULL) g_signal_handler_disconnect(priv->stage, priv->captureID); priv->captureID=0; priv->stage=NULL; } if(priv->longPressID!=0) { g_source_remove(priv->longPressID); priv->longPressID=0; } /* Reset state of this action */ _xfdashboard_click_action_set_pressed(self, FALSE); _xfdashboard_click_action_set_held(self, FALSE); /* Connect signals */ if(inActor!=NULL) { priv->eventID=g_signal_connect_swapped(inActor, "event", G_CALLBACK(_xfdashboard_click_action_on_event), self); } /* Call parent's class method */ if(CLUTTER_ACTOR_META_CLASS(xfdashboard_click_action_parent_class)->set_actor) { CLUTTER_ACTOR_META_CLASS(xfdashboard_click_action_parent_class)->set_actor(inActorMeta, inActor); } }
/* Proxy ClickAction signals */ static void _xfdashboard_button_clicked(XfdashboardClickAction *inAction, ClutterActor *self, gpointer inUserData) { g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(inAction)); g_return_if_fail(XFDASHBOARD_IS_BUTTON(self)); /* Only emit signal if click was perform with left button */ if(xfdashboard_click_action_get_button(inAction)==XFDASHBOARD_CLICK_ACTION_LEFT_BUTTON) { /* Emit 'clicked' signal */ g_signal_emit(self, XfdashboardButtonSignals[SIGNAL_CLICKED], 0); } }
/* Set held state */ static void _xfdashboard_click_action_set_held(XfdashboardClickAction *self, gboolean isHeld) { XfdashboardClickActionPrivate *priv; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); priv=self->priv; /* Set value if changed */ isHeld=!!isHeld; if(priv->isHeld!=isHeld) { /* Set value */ priv->isHeld=isHeld; /* Notify about property change */ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardClickActionProperties[PROP_HELD]); } }
/** * xfdashboard_click_action_release: * @self: A #XfdashboardClickAction * * Emulates a release of the pointer button, which ungrabs the pointer * and unsets the #XfdashboardClickAction:pressed state. * * This function will also cancel the long press gesture if one was * initiated. * * This function is useful to break a grab, for instance after a certain * amount of time has passed. */ void xfdashboard_click_action_release(XfdashboardClickAction *self) { XfdashboardClickActionPrivate *priv; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); priv=self->priv; /* Only release pointer button if it is held by this action */ if(!priv->isHeld) return; /* Disconnect signal handlers */ if(priv->captureID!=0) { g_signal_handler_disconnect(priv->stage, priv->captureID); priv->captureID=0; } /* Reset state of this action */ _xfdashboard_click_action_cancel_long_press(self); _xfdashboard_click_action_set_held(self, FALSE); _xfdashboard_click_action_set_pressed(self, FALSE); }
/* Cancel long-press handling */ static void _xfdashboard_click_action_cancel_long_press(XfdashboardClickAction *self) { XfdashboardClickActionPrivate *priv; g_return_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self)); priv=self->priv; /* Remove signals/sources and emit cancel signal */ if(priv->longPressID!=0) { ClutterActor *actor; gboolean result; /* Remove source */ g_source_remove(priv->longPressID); priv->longPressID=0; /* Emit signal */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(self)); g_signal_emit(self, XfdashboardClickActionSignals[SIGNAL_LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_CANCEL, &result); } }
/** * xfdashboard_click_action_get_state: * @self: A #XfdashboardClickAction * * Retrieves the modifier state of the click action. * * Return value: The modifier state parameter or 0 */ ClutterModifierType xfdashboard_click_action_get_state(XfdashboardClickAction *self) { g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), 0); return(self->priv->modifierState); }
/** * xfdashboard_click_action_get_button: * @self: A #XfdashboardClickAction * * Retrieves the button that was pressed. * * Return value: The button value */ guint xfdashboard_click_action_get_button(XfdashboardClickAction *self) { g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), 0); return(self->priv->pressButton); }
/* An event was received */ static gboolean _xfdashboard_click_action_on_event(XfdashboardClickAction *self, ClutterEvent *inEvent, gpointer inUserData) { XfdashboardClickActionPrivate *priv; gboolean hasButton; ClutterActor *actor; g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), CLUTTER_EVENT_PROPAGATE); g_return_val_if_fail(CLUTTER_IS_ACTOR(inUserData), CLUTTER_EVENT_PROPAGATE); priv=self->priv; hasButton=TRUE; actor=CLUTTER_ACTOR(inUserData); /* Check if actor is enabled to handle events */ if(!clutter_actor_meta_get_enabled(CLUTTER_ACTOR_META(self))) return(CLUTTER_EVENT_PROPAGATE); /* Handle event */ switch(clutter_event_type(inEvent)) { case CLUTTER_TOUCH_BEGIN: hasButton=FALSE; case CLUTTER_BUTTON_PRESS: /* We only handle single clicks if it is pointer device */ if(hasButton && clutter_event_get_click_count(inEvent)!=1) { return(CLUTTER_EVENT_PROPAGATE); } /* Do we already held the press? */ if(priv->isHeld) return(CLUTTER_EVENT_STOP); /* Is the source of event a child of this actor. If not do * not handle this event but any other. */ if(!clutter_actor_contains(actor, clutter_event_get_source(inEvent))) { return(CLUTTER_EVENT_PROPAGATE); } /* Remember event data */ priv->pressButton=hasButton ? clutter_event_get_button(inEvent) : 0; priv->pressDeviceID=clutter_event_get_device_id(inEvent); priv->pressSequence=clutter_event_get_event_sequence(inEvent); priv->modifierState=clutter_event_get_state(inEvent); clutter_event_get_coords(inEvent, &priv->pressX, &priv->pressY); if(priv->longPressThreshold<0) { ClutterSettings *settings=clutter_settings_get_default(); g_object_get(settings, "dnd-drag-threshold", &priv->dragThreshold, NULL); } else priv->dragThreshold=priv->longPressThreshold; if(priv->stage==NULL) priv->stage=clutter_actor_get_stage(actor); /* Connect signals */ priv->captureID=g_signal_connect_object(priv->stage, "captured-event", G_CALLBACK(_xfdashboard_click_action_on_captured_event), self, G_CONNECT_AFTER | G_CONNECT_SWAPPED); /* Set state of this action */ _xfdashboard_click_action_set_pressed(self, TRUE); _xfdashboard_click_action_set_held(self, TRUE); _xfdashboard_click_action_query_long_press(self); break; case CLUTTER_ENTER: _xfdashboard_click_action_set_pressed(self, priv->isHeld); break; case CLUTTER_LEAVE: _xfdashboard_click_action_set_pressed(self, priv->isHeld); _xfdashboard_click_action_cancel_long_press(self); break; default: break; } return(CLUTTER_EVENT_PROPAGATE); }
/* An event was captured */ static gboolean _xfdashboard_click_action_on_captured_event(XfdashboardClickAction *self, ClutterEvent *inEvent, gpointer inUserData) { XfdashboardClickActionPrivate *priv; ClutterActor *stage G_GNUC_UNUSED; ClutterActor *actor; ClutterModifierType modifierState; gboolean hasButton; g_return_val_if_fail(XFDASHBOARD_IS_CLICK_ACTION(self), CLUTTER_EVENT_PROPAGATE); g_return_val_if_fail(CLUTTER_IS_ACTOR(inUserData), CLUTTER_EVENT_PROPAGATE); priv=self->priv; stage=CLUTTER_ACTOR(inUserData); hasButton=TRUE; /* Handle captured event */ actor=clutter_actor_meta_get_actor(CLUTTER_ACTOR_META(self)); switch(clutter_event_type(inEvent)) { case CLUTTER_TOUCH_END: hasButton=FALSE; case CLUTTER_BUTTON_RELEASE: if(!priv->isHeld) return(CLUTTER_EVENT_STOP); if((hasButton && clutter_event_get_button(inEvent)!=priv->pressButton) || (hasButton && clutter_event_get_click_count(inEvent)!=1) || clutter_event_get_device_id(inEvent)!=priv->pressDeviceID || clutter_event_get_event_sequence(inEvent)!=priv->pressSequence) { return(CLUTTER_EVENT_PROPAGATE); } _xfdashboard_click_action_set_held(self, FALSE); _xfdashboard_click_action_cancel_long_press(self); /* Disconnect the capture */ if(priv->captureID!=0) { g_signal_handler_disconnect(priv->stage, priv->captureID); priv->captureID = 0; } if(priv->longPressID!=0) { g_source_remove(priv->longPressID); priv->longPressID=0; } if(!clutter_actor_contains(actor, clutter_event_get_source(inEvent))) { return(CLUTTER_EVENT_PROPAGATE); } /* Exclude any button-mask so that we can compare * the press and release states properly */ modifierState=clutter_event_get_state(inEvent) & ~(CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); /* If press and release states don't match we simply ignore * modifier keys. i.e. modifier keys are expected to be pressed * throughout the whole click */ if(modifierState!=priv->modifierState) priv->modifierState=0; _xfdashboard_click_action_set_pressed(self, FALSE); g_signal_emit(self, XfdashboardClickActionSignals[SIGNAL_CLICKED], 0, actor); break; case CLUTTER_MOTION: case CLUTTER_TOUCH_UPDATE: { gfloat motionX, motionY; gfloat deltaX, deltaY; if(!priv->isHeld) return(CLUTTER_EVENT_PROPAGATE); clutter_event_get_coords (inEvent, &motionX, &motionY); deltaX=ABS(motionX-priv->pressX); deltaY=ABS(motionY-priv->pressY); if(deltaX>priv->dragThreshold || deltaY>priv->dragThreshold) { _xfdashboard_click_action_cancel_long_press(self); } } break; default: break; } /* This is line changed in returning CLUTTER_EVENT_PROPAGATE * instead of CLUTTER_EVENT_STOP */ return(CLUTTER_EVENT_PROPAGATE); }