static void
set_state(AvatarManipulation *self, AvatarManipulationState state)
{
    // note: this funciton does not check if the state transition is valid, this is assumed to have
    // been done by the caller
    AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self);

    // save prev state
    priv->last_state = priv->state;

    switch (state) {
        case AVATAR_MANIPULATION_STATE_CURRENT:
        {
            /* get the current or default profile avatar */
            auto photo = GlobalInstances::pixmapManipulator().contactPhoto(
                            ProfileModel::instance().selectedProfile()->person(),
                            QSize(AVATAR_WIDTH, AVATAR_HEIGHT),
                            false);
            std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>();

            if (photo.isValid()) {
                gtk_image_set_from_pixbuf (GTK_IMAGE(priv->image_avatar),  pixbuf_photo.get());
            } else {
                g_warning("invlid pixbuf");
            }

            gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_avatar");

            /* available actions: start camera (if available) or choose image */
            if (Video::DeviceModel::instance().rowCount() > 0) {
                // TODO: update if a video device gets inserted while in this state
                gtk_widget_set_visible(priv->button_start_camera, true);
            }
            gtk_widget_set_visible(priv->button_box_current, true);
            gtk_widget_set_visible(priv->button_box_photo,   false);
            gtk_widget_set_visible(priv->button_box_edit,    false);

            /* make sure video widget and camera is not running */
            if (priv->video_started_by_avatar_manipulation)
                Video::PreviewManager::instance().stopPreview();
            if (priv->video_widget) {
                gtk_container_remove(GTK_CONTAINER(priv->frame_video), priv->video_widget);
                priv->video_widget = NULL;
            }
        }
        break;
        case AVATAR_MANIPULATION_STATE_PHOTO:
        {
            // start the video; if its not available we should not be in this state
            priv->video_widget = video_widget_new();
            g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self);
            gtk_widget_set_vexpand_set(priv->video_widget, FALSE);
            gtk_widget_set_hexpand_set(priv->video_widget, FALSE);
            gtk_container_add(GTK_CONTAINER(priv->frame_video), priv->video_widget);
            gtk_widget_set_visible(priv->video_widget, true);
            gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth");


            /* local renderer, but set as "remote" so that it takes up the whole screen */
            video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget),
                                           Video::PreviewManager::instance().previewRenderer(),
                                           VIDEO_RENDERER_REMOTE);

            if (!Video::PreviewManager::instance().isPreviewing()) {
                priv->video_started_by_avatar_manipulation = TRUE;
                Video::PreviewManager::instance().startPreview();
            } else {
                priv->video_started_by_avatar_manipulation = FALSE;
            }

            /* available actions: take snapshot, return*/
            gtk_widget_set_visible(priv->button_box_current, false);
            gtk_widget_set_visible(priv->button_box_photo,   true);
            gtk_widget_set_visible(priv->button_box_edit,    false);
        }
        break;
        case AVATAR_MANIPULATION_STATE_EDIT:
        {
            /* make sure video widget and camera is not running */
            if (priv->video_started_by_avatar_manipulation)
                Video::PreviewManager::instance().stopPreview();
            if (priv->video_widget) {
                gtk_container_remove(GTK_CONTAINER(priv->frame_video), priv->video_widget);
                priv->video_widget = NULL;
            }

            /* available actions: set avatar, return */
            gtk_widget_set_visible(priv->button_box_current, false);
            gtk_widget_set_visible(priv->button_box_photo,   false);
            gtk_widget_set_visible(priv->button_box_edit,    true);

            gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_edit_view");
        }
        break;
    }

    priv->state = state;
}
static void
current_call_view_init(CurrentCallView *view)
{
    gtk_widget_init_template(GTK_WIDGET(view));

    CurrentCallViewPrivate *priv = CURRENT_CALL_VIEW_GET_PRIVATE(view);

    /* create video widget and overlay the call info and controls on it */
    priv->video_widget = video_widget_new();
    gtk_container_add(GTK_CONTAINER(priv->frame_video), priv->video_widget);
    gtk_widget_show_all(priv->frame_video);

    auto stage = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(priv->video_widget));
    auto actor_info = gtk_clutter_actor_new_with_contents(priv->hbox_call_info);
    auto actor_controls = gtk_clutter_actor_new_with_contents(priv->hbox_call_controls);

    clutter_actor_add_child(stage, actor_info);
    clutter_actor_set_x_align(actor_info, CLUTTER_ACTOR_ALIGN_FILL);
    clutter_actor_set_y_align(actor_info, CLUTTER_ACTOR_ALIGN_START);

    clutter_actor_add_child(stage, actor_controls);
    clutter_actor_set_x_align(actor_controls, CLUTTER_ACTOR_ALIGN_CENTER);
    clutter_actor_set_y_align(actor_controls, CLUTTER_ACTOR_ALIGN_END);

    /* add fade in and out states to the info and controls */
    priv->time_last_mouse_motion = g_get_monotonic_time();
    priv->fade_info = create_fade_out_transition();
    priv->fade_controls = create_fade_out_transition();
    clutter_actor_add_transition(actor_info, "fade_info", priv->fade_info);
    clutter_actor_add_transition(actor_controls, "fade_controls", priv->fade_controls);
    clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_info), CLUTTER_TIMELINE_BACKWARD);
    clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_controls), CLUTTER_TIMELINE_BACKWARD);
    clutter_timeline_stop(CLUTTER_TIMELINE(priv->fade_info));
    clutter_timeline_stop(CLUTTER_TIMELINE(priv->fade_controls));

    /* have a timer check every 1 second if the controls should fade out */
    priv->timer_fade = g_timeout_add(1000, (GSourceFunc)timeout_check_last_motion_event, view);

    /* connect to the mouse motion event to reset the last moved time */
    g_signal_connect_swapped(priv->video_widget, "motion-notify-event", G_CALLBACK(mouse_moved), view);
    g_signal_connect_swapped(priv->video_widget, "button-press-event", G_CALLBACK(mouse_moved), view);
    g_signal_connect_swapped(priv->video_widget, "button-release-event", G_CALLBACK(mouse_moved), view);

    /* manually handle the focus of the video widget to be able to focus on the call controls */
    g_signal_connect(priv->video_widget, "focus", G_CALLBACK(video_widget_focus), view);

    /* toggle whether or not the chat is displayed */
    g_signal_connect(priv->togglebutton_chat, "toggled", G_CALLBACK(chat_toggled), view);

    /* bind the chat orientation to the gsetting */
    priv->settings = g_settings_new_full(get_ring_schema(), NULL, NULL);
    g_settings_bind_with_mapping(priv->settings, "chat-pane-horizontal",
                                 priv->paned_call, "orientation",
                                 G_SETTINGS_BIND_GET,
                                 map_boolean_to_orientation,
                                 nullptr, nullptr, nullptr);

    g_signal_connect(priv->scalebutton_quality, "value-changed", G_CALLBACK(quality_changed), view);
    /* customize the quality button scale */
    if (auto scale_box = gtk_scale_button_get_box(GTK_SCALE_BUTTON(priv->scalebutton_quality))) {
        priv->checkbutton_autoquality = gtk_check_button_new_with_label(C_("Enable automatic video quality", "Auto"));
        gtk_widget_show(priv->checkbutton_autoquality);
        gtk_box_pack_start(GTK_BOX(scale_box), priv->checkbutton_autoquality, FALSE, TRUE, 0);
        g_signal_connect(priv->checkbutton_autoquality, "toggled", G_CALLBACK(autoquality_toggled), view);
        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->checkbutton_autoquality), TRUE);
    }
    if (auto scale = gtk_scale_button_get_scale(GTK_SCALE_BUTTON(priv->scalebutton_quality))) {
        g_signal_connect(scale, "button-press-event", G_CALLBACK(quality_button_pressed), view);
        g_signal_connect(scale, "button-release-event", G_CALLBACK(quality_button_released), view);
    }
}