예제 #1
0
파일: mx-expander.c 프로젝트: 3v1n0/mx
static void
mx_expander_update (MxExpander *expander)
{
  MxExpanderPrivate *priv = expander->priv;

  if (priv->expanded)
    {
      clutter_actor_set_name (priv->arrow, "mx-expander-arrow-open");
      mx_stylable_set_style_class (MX_STYLABLE (expander), "open-expander");
    }
  /* closed state is set when animation is finished */

  if (!priv->child)
    return;

  /* setup and start the expansion animation */
  if (!priv->expanded)
    {
      clutter_actor_hide (priv->child);
      clutter_timeline_set_direction (priv->timeline,
                                      CLUTTER_TIMELINE_BACKWARD);
    }
  else
    {
      clutter_timeline_set_direction (priv->timeline,
                                      CLUTTER_TIMELINE_FORWARD);
    }


  if (!clutter_timeline_is_playing (priv->timeline))
    clutter_timeline_rewind (priv->timeline);

  clutter_timeline_start (priv->timeline);
}
예제 #2
0
static void
mex_content_box_toggle_open (MexContentBox *box)
{
  MexContentBoxPrivate *priv = box->priv;
  gboolean close_notified, next_is_open;
  const gchar *mimetype;

  /* search history items should not appear in the "open" state */
  mimetype = mex_content_get_metadata (priv->content,
                                       MEX_CONTENT_METADATA_MIMETYPE);
  if (!g_strcmp0 (mimetype, "x-mex/search"))
    return;


  /* if the close animation was cancelled then no notify for the closed state
   * will have been sent, therefore notify for the opened state does not need
   * to be emitted */
  close_notified = (!priv->is_open
                    && !clutter_timeline_is_playing (priv->timeline));

  next_is_open = !priv->is_open;

  if (next_is_open)
    {
      /* opening */
      clutter_timeline_set_direction (priv->timeline,
                                      CLUTTER_TIMELINE_FORWARD);
      mx_stylable_set_style_class (MX_STYLABLE (box), "open");

      /* refresh the action list */
      mex_content_view_set_content (MEX_CONTENT_VIEW (priv->action_list),
                                    priv->content);

      priv->extras_visible = TRUE;
      if (close_notified)
        g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_OPEN]);

      mex_push_focus (MX_FOCUSABLE (priv->action_list));
    }
  else
    {
      priv->is_closing = TRUE;

      /* closing */
      mex_push_focus (MX_FOCUSABLE (priv->tile));
      clutter_timeline_set_direction (priv->timeline,
                                      CLUTTER_TIMELINE_BACKWARD);

      priv->is_closing = FALSE;
      priv->extras_visible = TRUE;
    }

  if (!clutter_timeline_is_playing (priv->timeline))
    clutter_timeline_rewind (priv->timeline);

  clutter_timeline_start (priv->timeline);

  priv->is_open = next_is_open;
}
예제 #3
0
파일: mx-toggle.c 프로젝트: ManMower/mx
void
mx_toggle_set_active (MxToggle *toggle,
                      gboolean  active)
{
  MxTogglePrivate *priv;

  g_return_if_fail (MX_IS_TOGGLE (toggle));

  priv = toggle->priv;

  if (priv->active != active
      || (priv->position > 0 && priv->position < 1))
    {
      ClutterTimeline *timeline;

      priv->active = active;

      if (active)
        mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), "checked");
      else
        mx_stylable_set_style_pseudo_class (MX_STYLABLE (toggle), NULL);

      g_object_notify (G_OBJECT (toggle), "active");


      /* don't run an animation if the actor is not mapped */
      if (!CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR (toggle)))
        {
          priv->position = (active) ? 1 : 0;
          return;
        }

      timeline = clutter_alpha_get_timeline (priv->alpha);

      if (active)
        clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_FORWARD);
      else
        clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_BACKWARD);

      if (clutter_timeline_is_playing (timeline))
        return;

      clutter_timeline_rewind (timeline);

      if (priv->drag_offset > -1)
        {
          clutter_alpha_set_mode (priv->alpha, CLUTTER_LINEAR);
          clutter_timeline_advance (timeline, priv->position * 300);
        }
      else
        {
          clutter_alpha_set_mode (priv->alpha, CLUTTER_EASE_IN_OUT_CUBIC);
        }

      clutter_timeline_start (timeline);
    }
}
예제 #4
0
static void reverse_timeline (ClutterTimeline *timeline,
                              gpointer         data)
{
  ClutterTimelineDirection direction = clutter_timeline_get_direction (timeline);
  if (direction == CLUTTER_TIMELINE_FORWARD)
    clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_BACKWARD);
  else
    clutter_timeline_set_direction (timeline, CLUTTER_TIMELINE_FORWARD);
  clutter_timeline_start (timeline);
}
예제 #5
0
static void
clutter_timeline_set_property (GObject      *object,
			       guint         prop_id,
			       const GValue *value,
			       GParamSpec   *pspec)
{
  ClutterTimeline        *timeline;
  ClutterTimelinePrivate *priv;

  timeline = CLUTTER_TIMELINE(object);
  priv = timeline->priv;

  switch (prop_id)
    {
    case PROP_LOOP:
      priv->loop = g_value_get_boolean (value);
      break;

    case PROP_DELAY:
      priv->delay = g_value_get_uint (value);
      break;

    case PROP_DURATION:
      clutter_timeline_set_duration (timeline, g_value_get_uint (value));
      break;

    case PROP_DIRECTION:
      clutter_timeline_set_direction (timeline, g_value_get_enum (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}
예제 #6
0
/* each time the timeline animating the label completes, swap the direction */
static void
timeline_completed (ClutterTimeline *timeline,
                    gpointer         user_data)
{
  clutter_timeline_set_direction (timeline,
                                  !clutter_timeline_get_direction (timeline));
  clutter_timeline_start (timeline);
}
예제 #7
0
파일: mx-adjustment.c 프로젝트: ManMower/mx
/**
 * mx_adjustment_interpolate:
 * @adjustment: A #MxAdjustment
 * @value: A #gdouble
 * @duration: duration in milliseconds
 * @mode: A #ClutterAnimationMode
 *
 * Interpolate #MxAdjustment:value to the new value specified by @value, using
 * the mode and duration given.
 */
void
mx_adjustment_interpolate (MxAdjustment *adjustment,
                           gdouble       value,
                           guint         duration,
                           gulong        mode)
{
  MxAdjustmentPrivate *priv = adjustment->priv;

  g_return_if_fail (isfinite (value));

  if (duration <= 1)
    {
      stop_interpolation (adjustment);
      mx_adjustment_set_value (adjustment, value);
      return;
    }

  priv->old_position = priv->value;
  priv->new_position = value;

  if (!priv->interpolation)
    {
      priv->interpolation = clutter_timeline_new (duration);

      g_signal_connect (priv->interpolation,
                        "new-frame",
                        G_CALLBACK (interpolation_new_frame_cb),
                        adjustment);
      g_signal_connect (priv->interpolation,
                        "completed",
                        G_CALLBACK (interpolation_completed_cb),
                        adjustment);
    }
  else
    {
      /* Extend the animation if it gets interrupted, otherwise frequent calls
       * to this function will end up with no advancements until the calls
       * finish (as the animation never gets a chance to start).
       */
      clutter_timeline_set_direction (priv->interpolation,
                                      CLUTTER_TIMELINE_FORWARD);
      clutter_timeline_rewind (priv->interpolation);
      clutter_timeline_set_duration (priv->interpolation, duration);
    }

  if (priv->interpolate_alpha)
    g_object_unref (priv->interpolate_alpha);

  priv->interpolate_alpha = clutter_alpha_new_full (priv->interpolation,
                                                    mode);

  clutter_timeline_start (priv->interpolation);
}
예제 #8
0
static void
reverse_timeline (ClutterTimeline *timeline)
{
  ClutterTimelineDirection dir = clutter_timeline_get_direction (timeline);

  if (dir == CLUTTER_TIMELINE_FORWARD)
    dir = CLUTTER_TIMELINE_BACKWARD;
  else
    dir = CLUTTER_TIMELINE_FORWARD;

  clutter_timeline_set_direction (timeline, dir);
}
예제 #9
0
static gboolean
mouse_moved(CurrentCallView *self)
{
    g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), FALSE);
    auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);

    priv->time_last_mouse_motion = g_get_monotonic_time();

    // since the mouse moved, make sure the controls are shown
    if (clutter_timeline_get_direction(CLUTTER_TIMELINE(priv->fade_info)) == CLUTTER_TIMELINE_FORWARD) {
        clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_info), CLUTTER_TIMELINE_BACKWARD);
        clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_controls), CLUTTER_TIMELINE_BACKWARD);
        if (!clutter_timeline_is_playing(CLUTTER_TIMELINE(priv->fade_info))) {
            clutter_timeline_rewind(CLUTTER_TIMELINE(priv->fade_info));
            clutter_timeline_rewind(CLUTTER_TIMELINE(priv->fade_controls));
            clutter_timeline_start(CLUTTER_TIMELINE(priv->fade_info));
            clutter_timeline_start(CLUTTER_TIMELINE(priv->fade_controls));
        }
    }

    return FALSE; // propogate event
}
예제 #10
0
static void
timeline_completed (ClutterTimeline *timeline)
{
  ClutterTimelineDirection direction;

  direction = clutter_timeline_get_direction (timeline);

  if (direction == CLUTTER_TIMELINE_FORWARD)
    direction = CLUTTER_TIMELINE_BACKWARD;
  else
    direction = CLUTTER_TIMELINE_FORWARD;

  clutter_timeline_set_direction (timeline, direction);
}
예제 #11
0
static gboolean
timeout_check_last_motion_event(CurrentCallView *self)
{
    g_return_val_if_fail(IS_CURRENT_CALL_VIEW(self), G_SOURCE_REMOVE);
    auto priv = CURRENT_CALL_VIEW_GET_PRIVATE(self);

    auto current_time = g_get_monotonic_time();
    if (current_time - priv->time_last_mouse_motion >= CONTROLS_FADE_TIMEOUT) {
        // timeout has passed, hide the controls
        if (clutter_timeline_get_direction(CLUTTER_TIMELINE(priv->fade_info)) == CLUTTER_TIMELINE_BACKWARD) {
            clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_info), CLUTTER_TIMELINE_FORWARD);
            clutter_timeline_set_direction(CLUTTER_TIMELINE(priv->fade_controls), CLUTTER_TIMELINE_FORWARD);
            if (!clutter_timeline_is_playing(CLUTTER_TIMELINE(priv->fade_info))) {
                clutter_timeline_rewind(CLUTTER_TIMELINE(priv->fade_info));
                clutter_timeline_rewind(CLUTTER_TIMELINE(priv->fade_controls));
                clutter_timeline_start(CLUTTER_TIMELINE(priv->fade_info));
                clutter_timeline_start(CLUTTER_TIMELINE(priv->fade_controls));
            }
        }
    }

    return G_SOURCE_CONTINUE;
}
예제 #12
0
static void
mex_tile_timeline_completed_cb (ClutterTimeline *timeline,
                                MexTile          *tile)
{
  ClutterTimelineDirection direction =
    clutter_timeline_get_direction (timeline);

  /* Reversing the direction and rewinding will make sure that on
   * completion, progress stays at 0.0 or 1.0, depending on the
   * direction of the timeline.
   */
  clutter_timeline_set_direction (timeline,
                                  (direction == CLUTTER_TIMELINE_FORWARD) ?
                                  CLUTTER_TIMELINE_BACKWARD :
                                  CLUTTER_TIMELINE_FORWARD);
  clutter_timeline_rewind (timeline);

  mex_tile_important_new_frame_cb (timeline, 0, tile);
}
예제 #13
0
파일: mx-adjustment.c 프로젝트: ManMower/mx
static void
interpolation_completed_cb (ClutterTimeline *timeline,
                            MxAdjustment    *adjustment)
{
  MxAdjustmentPrivate *priv = adjustment->priv;

  if (priv->elastic && priv->clamp_value)
    {
      if (clutter_timeline_get_direction (priv->interpolation) ==
            CLUTTER_TIMELINE_FORWARD)
        {
          clutter_timeline_set_direction (priv->interpolation,
                                          CLUTTER_TIMELINE_BACKWARD);
          clutter_timeline_set_duration (priv->interpolation, 250);
          clutter_timeline_rewind (priv->interpolation);

          if (priv->new_position < priv->lower)
            {
              priv->old_position = priv->lower;
              clutter_timeline_start (priv->interpolation);
            }
          else if (priv->new_position > (priv->upper - priv->page_size))
            {
              priv->old_position = priv->upper - priv->page_size;
              clutter_timeline_start (priv->interpolation);
            }
        }
      else
        {
          stop_interpolation (adjustment);
          mx_adjustment_set_value (adjustment, priv->old_position);
        }
    }
  else
    {
      stop_interpolation (adjustment);
      mx_adjustment_set_value (adjustment, priv->new_position);
    }

  g_signal_emit (adjustment, signals[INTERPOLATION_COMPLETED], 0);
}
예제 #14
0
void
mex_tile_set_important (MexTile  *tile,
                        gboolean  important)
{
  MexTilePrivate *priv;

  g_return_if_fail (MEX_IS_TILE (tile));

  priv = tile->priv;
  if (priv->important != important)
    {
      priv->important = important;

      g_object_notify (G_OBJECT (tile), "important");

      mx_stylable_set_style_class (MX_STYLABLE (tile),
                                   important ? "Important" : NULL);

      if (clutter_timeline_is_playing (priv->timeline))
        clutter_timeline_set_direction (priv->timeline, important ?
                                        CLUTTER_TIMELINE_FORWARD :
                                        CLUTTER_TIMELINE_BACKWARD);
      else
        {
          if (CLUTTER_ACTOR_IS_MAPPED (tile))
            {
              clutter_timeline_rewind (priv->timeline);
              clutter_timeline_start (priv->timeline);
            }
          else
            {
              clutter_timeline_advance (priv->timeline, DURATION);
              mex_tile_important_new_frame_cb (priv->timeline, 0, tile);
              mex_tile_timeline_completed_cb (priv->timeline, tile);
            }
        }
    }
}
static void
mx_label_allocate (ClutterActor          *actor,
                   const ClutterActorBox *box,
                   ClutterAllocationFlags flags)
{
  MxLabelPrivate *priv = MX_LABEL (actor)->priv;
  gboolean label_did_fade = priv->label_should_fade;

  ClutterActorClass *parent_class;
  ClutterActorBox child_box;
  gboolean x_fill, y_fill;
  gfloat avail_width;

  parent_class = CLUTTER_ACTOR_CLASS (mx_label_parent_class);
  parent_class->allocate (actor, box, flags);

  mx_widget_get_available_area (MX_WIDGET (actor), box, &child_box);
  avail_width = child_box.x2 - child_box.x1;

  /* The default behaviour of ClutterText is to align to the
   * top-left when it gets more space than is needed. Because
   * of this behaviour, if we're aligning to the left, we can
   * assign all our horizontal space to the label without
   * measuring it (i.e. x-fill), and the same applies for
   * aligning to the top and vertical space.
   */
  x_fill = (priv->x_align == MX_ALIGN_START) ? TRUE : FALSE;
  y_fill = (priv->y_align == MX_ALIGN_START) ? TRUE : FALSE;

  mx_allocate_align_fill (priv->label, &child_box, priv->x_align,
                          priv->y_align, x_fill, y_fill);

  priv->label_should_fade = FALSE;

  if (priv->fade_out)
    {
      /* If we're fading out, make sure the label has its full width
       * allocated. This ensures that the offscreen effect has the full
       * label inside its texture.
       */
      gfloat label_width;

      clutter_actor_get_preferred_width (priv->label, -1, NULL, &label_width);

      if (label_width > avail_width)
        {
          priv->label_should_fade = TRUE;
          child_box.x2 = child_box.x1 + label_width;
        }

      mx_fade_effect_set_bounds (MX_FADE_EFFECT (priv->fade_effect),
                                 0, 0, MIN (label_width, avail_width), 0);
    }

  /* Allocate the label */
  clutter_actor_allocate (priv->label, &child_box, flags);

  if (priv->show_tooltip)
    {
      PangoLayout *layout;
      const gchar *text;

      layout = clutter_text_get_layout (CLUTTER_TEXT (priv->label));

      if (pango_layout_is_ellipsized (layout))
        text = clutter_text_get_text (CLUTTER_TEXT (priv->label));
      else
        text = NULL;

      mx_widget_set_tooltip_text (MX_WIDGET (actor), text);
    }

  /* Animate in/out the faded end of the label */
  if (label_did_fade != priv->label_should_fade)
    {
      /* Begin/reverse the fading timeline when necessary */
      if (priv->label_should_fade)
        clutter_timeline_set_direction (priv->fade_timeline,
                                        CLUTTER_TIMELINE_FORWARD);
      else
        clutter_timeline_set_direction (priv->fade_timeline,
                                        CLUTTER_TIMELINE_BACKWARD);

      if (!clutter_timeline_is_playing (priv->fade_timeline))
        clutter_timeline_rewind (priv->fade_timeline);

      clutter_timeline_start (priv->fade_timeline);
    }
}
예제 #16
0
void
st_theme_node_transition_update (StThemeNodeTransition *transition,
                                 StThemeNode           *new_node)
{
  StThemeNodeTransitionPrivate *priv = transition->priv;
  StThemeNode *old_node;
  ClutterTimelineDirection direction;

  g_return_if_fail (ST_IS_THEME_NODE_TRANSITION (transition));
  g_return_if_fail (ST_IS_THEME_NODE (new_node));

  direction = clutter_timeline_get_direction (priv->timeline);
  old_node = (direction == CLUTTER_TIMELINE_FORWARD) ? priv->old_theme_node
                                                     : priv->new_theme_node;

  /* If the update is the reversal of the current transition,
   * we reverse the timeline.
   * Otherwise, we should initiate a new transition from the
   * current state to the new one; this is hard to do if the
   * transition is in an intermediate state, so we just cancel
   * the ongoing transition in that case.
   * Note that reversing a timeline before any time elapsed
   * results in the timeline's time position being set to the
   * full duration - this is not what we want, so we cancel the
   * transition as well in that case.
   */
  if (st_theme_node_equal (new_node, old_node))
    {
      if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
        {
          if (direction == CLUTTER_TIMELINE_FORWARD)
            clutter_timeline_set_direction (priv->timeline,
                                            CLUTTER_TIMELINE_BACKWARD);
          else
            clutter_timeline_set_direction (priv->timeline,
                                            CLUTTER_TIMELINE_FORWARD);
        }
      else
        {
          clutter_timeline_stop (priv->timeline);
          g_signal_emit (transition, signals[COMPLETED], 0);
        }
    }
  else
    {
      if (clutter_timeline_get_elapsed_time (priv->timeline) > 0)
        {
          clutter_timeline_stop (priv->timeline);
          g_signal_emit (transition, signals[COMPLETED], 0);
        }
      else
        {
          guint new_duration = st_theme_node_get_transition_duration (new_node);

          clutter_timeline_set_duration (priv->timeline, new_duration);

          /* If the change doesn't affect painting, we don't need to redraw,
           * but we still need to replace the node so that we properly share
           * caching with the painting that happens after the transition finishes.
           */
          if (!st_theme_node_paint_equal (priv->new_theme_node, new_node))
              priv->needs_setup = TRUE;

          g_object_unref (priv->new_theme_node);
          priv->new_theme_node = g_object_ref (new_node);
        }
    }
}
예제 #17
0
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);
    }
}