+static ClutterActor *
+empathy_call_window_create_preview_rectangle (EmpathyCallWindow *self,
+ ClutterActor **box,
+ ClutterBinAlignment x,
+ ClutterBinAlignment y)
+{
+ ClutterLayoutManager *layout1, *layout2;
+ ClutterActor *rectangle;
+ ClutterActor *box1, *box2;
+
+ layout1 = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_START);
+
+ box1 = clutter_box_new (layout1);
+
+ *box = box1;
+
+ rectangle = empathy_rounded_rectangle_new (
+ SELF_VIDEO_SECTION_WIDTH + 5,
+ SELF_VIDEO_SECTION_HEIGHT + 5);
+
+ clutter_actor_set_size (box1,
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN +
+ FLOATING_TOOLBAR_HEIGHT + FLOATING_TOOLBAR_SPACING);
+
+ layout2 = clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER,
+ CLUTTER_BIN_ALIGNMENT_CENTER);
+
+ /* We have a box with the margins and the video in the middle inside
+ * a bigger box with an extra bottom margin so we're not on top of
+ * the floating toolbar. */
+ box2 = clutter_box_new (layout2);
+
+ clutter_actor_set_size (box2,
+ SELF_VIDEO_SECTION_WIDTH + 2 * SELF_VIDEO_SECTION_MARGIN,
+ SELF_VIDEO_SECTION_HEIGHT + 2 * SELF_VIDEO_SECTION_MARGIN);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (box1), box2);
+ clutter_container_add_actor (CLUTTER_CONTAINER (box2), rectangle);
+
+ clutter_bin_layout_add (CLUTTER_BIN_LAYOUT (self->priv->video_layout),
+ box1, x, y);
+
+ clutter_actor_hide (rectangle);
+
+ return rectangle;
+}
+
+static void
+empathy_call_window_create_preview_rectangles (EmpathyCallWindow *self)
+{
+ self->priv->preview_rectangle1 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box1,
+ CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_START);
+ self->priv->preview_rectangle2 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box2,
+ CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_END);
+ self->priv->preview_rectangle3 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box3,
+ CLUTTER_BIN_ALIGNMENT_END, CLUTTER_BIN_ALIGNMENT_START);
+ self->priv->preview_rectangle4 =
+ empathy_call_window_create_preview_rectangle (self,
+ &self->priv->preview_rectangle_box4,
+ CLUTTER_BIN_ALIGNMENT_END, CLUTTER_BIN_ALIGNMENT_END);
+}
+
+static void
+empathy_call_window_show_preview_rectangles (EmpathyCallWindow *self,
+ gboolean show)
+{
+ g_object_set (self->priv->preview_rectangle1, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle2, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle3, "visible", show, NULL);
+ g_object_set (self->priv->preview_rectangle4, "visible", show, NULL);
+}
+
+static void
+empathy_call_window_get_preview_coordinates (EmpathyCallWindow *self,
+ PreviewPosition pos,
+ guint *x,
+ guint *y)
+{
+ guint ret_x = 0, ret_y = 0;
+ ClutterGeometry box;
+
+ if (!clutter_actor_has_allocation (self->priv->video_box))
+ goto out;
+
+ clutter_actor_get_geometry (self->priv->video_box, &box);
+
+ switch (pos)
+ {
+ case PREVIEW_POS_TOP_LEFT:
+ ret_x = ret_y = SELF_VIDEO_SECTION_MARGIN;
+ break;
+ case PREVIEW_POS_TOP_RIGHT:
+ ret_x = box.width - SELF_VIDEO_SECTION_MARGIN
+ - SELF_VIDEO_SECTION_WIDTH;
+ ret_y = SELF_VIDEO_SECTION_MARGIN;
+ break;
+ case PREVIEW_POS_BOTTOM_LEFT:
+ ret_x = SELF_VIDEO_SECTION_MARGIN;
+ ret_y = box.height - SELF_VIDEO_SECTION_MARGIN
+ - SELF_VIDEO_SECTION_HEIGHT
+ - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING;
+ break;
+ case PREVIEW_POS_BOTTOM_RIGHT:
+ ret_x = box.width - SELF_VIDEO_SECTION_MARGIN
+ - SELF_VIDEO_SECTION_WIDTH;
+ ret_y = box.height - SELF_VIDEO_SECTION_MARGIN
+ - SELF_VIDEO_SECTION_HEIGHT - FLOATING_TOOLBAR_HEIGHT
+ - FLOATING_TOOLBAR_SPACING;
+ break;
+ default:
+ g_warn_if_reached ();
+ }
+
+out:
+ if (x != NULL)
+ *x = ret_x;
+
+ if (y != NULL)
+ *y = ret_y;
+}
+
+static PreviewPosition
+empathy_call_window_get_preview_position (EmpathyCallWindow *self,
+ gfloat event_x,
+ gfloat event_y)
+{
+ ClutterGeometry box;
+ PreviewPosition pos = PREVIEW_POS_NONE;
+
+ if (!clutter_actor_has_allocation (self->priv->video_box))
+ return pos;
+
+ clutter_actor_get_geometry (self->priv->video_box, &box);
+
+ if (0 + SELF_VIDEO_SECTION_MARGIN <= event_x &&
+ event_x <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ 0 + SELF_VIDEO_SECTION_MARGIN <= event_y &&
+ event_y <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_TOP_LEFT;
+ }
+ else if (box.width - SELF_VIDEO_SECTION_MARGIN >= event_x &&
+ event_x >= (box.width - SELF_VIDEO_SECTION_MARGIN - (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ 0 + SELF_VIDEO_SECTION_MARGIN <= event_y &&
+ event_y <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_TOP_RIGHT;
+ }
+ else if (0 + SELF_VIDEO_SECTION_MARGIN <= event_x &&
+ event_x <= (0 + SELF_VIDEO_SECTION_MARGIN + (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING >= event_y &&
+ event_y >= (box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING - (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_BOTTOM_LEFT;
+ }
+ else if (box.width - SELF_VIDEO_SECTION_MARGIN >= event_x &&
+ event_x >= (box.width - SELF_VIDEO_SECTION_MARGIN - (gint) SELF_VIDEO_SECTION_WIDTH) &&
+ box.height - SELF_VIDEO_SECTION_MARGIN - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING >= event_y &&
+ event_y >= (box.height - SELF_VIDEO_SECTION_MARGIN - FLOATING_TOOLBAR_HEIGHT - FLOATING_TOOLBAR_SPACING - (gint) SELF_VIDEO_SECTION_HEIGHT))
+ {
+ pos = PREVIEW_POS_BOTTOM_RIGHT;
+ }
+
+ return pos;
+}
+
+static ClutterActor *
+empathy_call_window_get_preview_rectangle (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterActor *rectangle;
+
+ switch (pos)
+ {
+ case PREVIEW_POS_TOP_LEFT:
+ rectangle = self->priv->preview_rectangle1;
+ break;
+ case PREVIEW_POS_TOP_RIGHT:
+ rectangle = self->priv->preview_rectangle3;
+ break;
+ case PREVIEW_POS_BOTTOM_LEFT:
+ rectangle = self->priv->preview_rectangle2;
+ break;
+ case PREVIEW_POS_BOTTOM_RIGHT:
+ rectangle = self->priv->preview_rectangle4;
+ break;
+ default:
+ rectangle = NULL;
+ }
+
+ return rectangle;
+}
+
+static void
+empathy_call_window_move_video_preview (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (self->priv->video_layout);
+
+ DEBUG ("moving the video preview to %d", pos);
+
+ self->priv->preview_pos = pos;
+
+ switch (pos)
+ {
+ case PREVIEW_POS_TOP_LEFT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_START,
+ CLUTTER_BIN_ALIGNMENT_START);
+ break;
+ case PREVIEW_POS_TOP_RIGHT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_START);
+ break;
+ case PREVIEW_POS_BOTTOM_LEFT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_START,
+ CLUTTER_BIN_ALIGNMENT_END);
+ break;
+ case PREVIEW_POS_BOTTOM_RIGHT:
+ clutter_bin_layout_set_alignment (layout,
+ self->priv->video_preview,
+ CLUTTER_BIN_ALIGNMENT_END,
+ CLUTTER_BIN_ALIGNMENT_END);
+ break;
+ default:
+ g_warn_if_reached ();
+ }
+
+ g_settings_set_enum (self->priv->settings, "camera-position", pos);
+}
+
+static void
+empathy_call_window_highlight_preview_rectangle (EmpathyCallWindow *self,
+ PreviewPosition pos)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self, pos);
+
+ empathy_rounded_rectangle_set_border_width (
+ EMPATHY_ROUNDED_RECTANGLE (rectangle), 5);
+ empathy_rounded_rectangle_set_border_color (
+ EMPATHY_ROUNDED_RECTANGLE (rectangle), CLUTTER_COLOR_Red);
+}
+
+static void
+empathy_call_window_darken_preview_rectangle (EmpathyCallWindow *self,
+ ClutterActor *rectangle)
+{
+ empathy_rounded_rectangle_set_border_width (
+ EMPATHY_ROUNDED_RECTANGLE (rectangle), 1);
+ empathy_rounded_rectangle_set_border_color (
+ EMPATHY_ROUNDED_RECTANGLE (rectangle), CLUTTER_COLOR_Black);
+}
+
+static void
+empathy_call_window_darken_preview_rectangles (EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ /* We don't want to darken the rectangle where the preview
+ * currently is. */
+
+ if (self->priv->preview_rectangle1 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle1);
+
+ if (self->priv->preview_rectangle2 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle2);
+
+ if (self->priv->preview_rectangle3 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle3);
+
+ if (self->priv->preview_rectangle4 != rectangle)
+ empathy_call_window_darken_preview_rectangle (self,
+ self->priv->preview_rectangle4);
+}
+
+static void
+empathy_call_window_preview_on_drag_begin_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat event_x,
+ gfloat event_y,
+ ClutterModifierType modifiers,
+ EmpathyCallWindow *self)
+{
+ ClutterActor *stage = clutter_actor_get_stage (actor);
+ gfloat rel_x, rel_y;
+
+ self->priv->drag_preview = clutter_clone_new (actor);
+
+ clutter_container_add_actor (CLUTTER_CONTAINER (stage),
+ self->priv->drag_preview);
+
+ clutter_actor_transform_stage_point (actor, event_x, event_y,
+ &rel_x, &rel_y);
+
+ clutter_actor_set_position (self->priv->drag_preview,
+ event_x - rel_x, event_y - rel_y);
+
+ clutter_drag_action_set_drag_handle (action,
+ self->priv->drag_preview);
+
+ clutter_actor_set_opacity (actor, 0);
+ clutter_actor_hide (self->priv->preview_shown_button);
+
+ empathy_call_window_show_preview_rectangles (self, TRUE);
+ empathy_call_window_darken_preview_rectangles (self);
+}
+
+static void
+empathy_call_window_on_animation_completed_cb (ClutterAnimation *animation,
+ ClutterActor *actor)
+{
+ clutter_actor_set_opacity (actor, 255);
+}
+
+static void
+empathy_call_window_preview_on_drag_end_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat event_x,
+ gfloat event_y,
+ ClutterModifierType modifiers,
+ EmpathyCallWindow *self)
+{
+ PreviewPosition pos;
+ guint x, y;
+
+ /* Get the position before destroying the drag actor, otherwise the
+ * preview_box allocation won't be valid and we won't be able to
+ * calculate the position. */
+ pos = empathy_call_window_get_preview_position (self, event_x, event_y);
+
+ empathy_call_window_get_preview_coordinates (self,
+ pos != PREVIEW_POS_NONE ? pos : self->priv->preview_pos,
+ &x, &y);
+
+ /* Move the preview to the destination and destroy it afterwards */
+ clutter_actor_animate (self->priv->drag_preview, CLUTTER_LINEAR, 500,
+ "x", (gfloat) x,
+ "y", (gfloat) y,
+ "signal-swapped-after::completed",
+ clutter_actor_destroy, self->priv->drag_preview,
+ "signal-swapped-after::completed",
+ clutter_actor_show, self->priv->preview_shown_button,
+ "signal::completed",
+ empathy_call_window_on_animation_completed_cb, actor,
+ NULL);
+
+ self->priv->drag_preview = NULL;
+
+ if (pos != PREVIEW_POS_NONE)
+ empathy_call_window_move_video_preview (self, pos);
+
+ empathy_call_window_show_preview_rectangles (self, FALSE);
+}
+
+static void
+empathy_call_window_preview_on_drag_motion_cb (ClutterDragAction *action,
+ ClutterActor *actor,
+ gfloat delta_x,
+ gfloat delta_y,
+ EmpathyCallWindow *self)
+{
+ PreviewPosition pos;
+ gfloat event_x, event_y;
+
+ clutter_drag_action_get_motion_coords (action, &event_x, &event_y);
+
+ pos = empathy_call_window_get_preview_position (self, event_x, event_y);
+
+ if (pos != PREVIEW_POS_NONE)
+ empathy_call_window_highlight_preview_rectangle (self, pos);
+ else
+ empathy_call_window_darken_preview_rectangles (self);
+}
+
+static gboolean
+empathy_call_window_preview_enter_event_cb (ClutterActor *actor,
+ ClutterCrossingEvent *event,
+ EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ empathy_call_window_highlight_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ clutter_actor_show (rectangle);
+
+ return FALSE;
+}
+
+static gboolean
+empathy_call_window_preview_leave_event_cb (ClutterActor *actor,
+ ClutterCrossingEvent *event,
+ EmpathyCallWindow *self)
+{
+ ClutterActor *rectangle;
+
+ rectangle = empathy_call_window_get_preview_rectangle (self,
+ self->priv->preview_pos);
+
+ empathy_call_window_darken_preview_rectangle (self, rectangle);
+
+ clutter_actor_hide (rectangle);
+
+ return FALSE;
+}
+