<commit_before>/*
 * mx-button.c: Plain button actor
 *
 * Copyright 2007 OpenedHand
 * Copyright 2008, 2009 Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Written by: <NAME> <<EMAIL>>
 *             <NAME> <<EMAIL>>
 *
 */

/**
 * SECTION:mx-button
 * @short_description: Button widget
 *
 * A button widget with support for either a text label or icon, toggle mode
 * and transitions effects between states.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <clutter/clutter.h>
#include <clutter/clutter-keysyms.h>

#include "mx-button.h"

#include "mx-marshal.h"
#include "mx-stylable.h"
#include "mx-style.h"
#include "mx-texture-frame.h"
#include "mx-texture-cache.h"
#include "mx-private.h"
#include "mx-enum-types.h"
#include "mx-focusable.h"

enum
{
  PROP_0,

  PROP_LABEL,
  PROP_TOGGLE,
  PROP_ACTIVE
};

enum
{
  CLICKED,

  LAST_SIGNAL
};

#define MX_BUTTON_GET_PRIVATE(obj)    \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_BUTTON, MxButtonPrivate))

struct _MxButtonPrivate
{
  gchar            *text;

  ClutterActor     *old_bg;
  gboolean          old_bg_parented; /* TRUE if we have adopted old_bg */

  guint8            old_opacity;

  guint             is_pressed : 1;
  guint             is_hover : 1;
  guint             is_checked : 1;
  guint             is_toggle : 1;

  ClutterAnimation *animation;

  ClutterActor *content_image;
};

static guint button_signals[LAST_SIGNAL] = { 0, };

static void mx_stylable_iface_init (MxStylableIface *iface);
static void mx_focusable_iface_init (MxFocusableIface *iface);

G_DEFINE_TYPE_WITH_CODE (MxButton, mx_button, MX_TYPE_BIN,
                         G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE,
                                                mx_stylable_iface_init)
                         G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE,
                                                mx_focusable_iface_init));

static void
mx_stylable_iface_init (MxStylableIface *iface)
{
  static gboolean is_initialized = FALSE;

  if (G_UNLIKELY (!is_initialized))
    {
      ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x00 };
      GParamSpec *pspec;

      is_initialized = TRUE;

      pspec = clutter_param_spec_color ("background-color",
                                        "Background Color",
                                        "The background color of an actor",
                                        &bg_color,
                                        G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_boxed ("x-mx-content-image",
                                   "Content Image",
                                   "Image used as the button",
                                   MX_TYPE_BORDER_IMAGE,
                                   G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_string ("x-mx-icon-name",
                                   "Icon name",
                                   "Named icon to place inside the button",
                                   NULL,
                                   G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_int ("x-mx-icon-size",
                                "Icon size",
                                "Size to use for icon",
                                1, G_MAXINT, 48,
                                G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);
    }
}

static MxFocusable*
mx_button_accept_focus (MxFocusable *focusable, MxFocusHint hint)
{
  /* FIXME: pretend hover is the same as focus for now */
  mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), "hover");
  MX_BUTTON (focusable)->priv->is_hover = TRUE;

  clutter_actor_grab_key_focus (CLUTTER_ACTOR (focusable));

  return focusable;
}

static MxFocusable*
mx_button_move_focus (MxFocusable *focusable,
                      MxDirection  direction,
                      MxFocusable *from)
{
  /* check if focus is being moved from us */
  if (focusable == from)
    {
      mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), "");
      MX_BUTTON (focusable)->priv->is_hover = FALSE;
    }

  return NULL;
}

static void
mx_focusable_iface_init (MxFocusableIface *iface)
{
  iface->accept_focus = mx_button_accept_focus;
  iface->move_focus = mx_button_move_focus;
}

static void
mx_button_update_label_style (MxButton *button)
{
  ClutterActor *label;

  label = mx_bin_get_child ((MxBin*) button);

  /* check the child is really a label */
  if (!CLUTTER_IS_TEXT (label))
    return;

  mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (button),
                                             CLUTTER_TEXT (label));
}

static void
mx_button_dispose_old_bg (MxButton *button)
{
  MxButtonPrivate *priv = button->priv;

  if (priv->content_image)
    {
      clutter_actor_unparent (priv->content_image);
      priv->content_image = NULL;
    }

  if (priv->old_bg)
    {
      if (priv->old_bg_parented)
        {
          clutter_actor_unparent (priv->old_bg);
          priv->old_bg_parented = FALSE;
        }
      g_object_unref (priv->old_bg);<commit_msg>[button] Add a temporary solution to the animation/dispose race<commit_after>/*
 * mx-button.c: Plain button actor
 *
 * Copyright 2007 OpenedHand
 * Copyright 2008, 2009 Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU Lesser General Public License,
 * version 2.1, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Written by: <NAME> <<EMAIL>>
 *             <NAME> <<EMAIL>>
 *
 */

/**
 * SECTION:mx-button
 * @short_description: Button widget
 *
 * A button widget with support for either a text label or icon, toggle mode
 * and transitions effects between states.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <clutter/clutter.h>
#include <clutter/clutter-keysyms.h>

#include "mx-button.h"

#include "mx-marshal.h"
#include "mx-stylable.h"
#include "mx-style.h"
#include "mx-texture-frame.h"
#include "mx-texture-cache.h"
#include "mx-private.h"
#include "mx-enum-types.h"
#include "mx-focusable.h"

enum
{
  PROP_0,

  PROP_LABEL,
  PROP_TOGGLE,
  PROP_ACTIVE
};

enum
{
  CLICKED,

  LAST_SIGNAL
};

#define MX_BUTTON_GET_PRIVATE(obj)    \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MX_TYPE_BUTTON, MxButtonPrivate))

struct _MxButtonPrivate
{
  gchar            *text;

  ClutterActor     *old_bg;
  gboolean          old_bg_parented; /* TRUE if we have adopted old_bg */

  guint8            old_opacity;

  guint             is_pressed : 1;
  guint             is_hover : 1;
  guint             is_checked : 1;
  guint             is_toggle : 1;

  ClutterAnimation *animation;

  ClutterActor *content_image;
};

static guint button_signals[LAST_SIGNAL] = { 0, };

static void mx_stylable_iface_init (MxStylableIface *iface);
static void mx_focusable_iface_init (MxFocusableIface *iface);

G_DEFINE_TYPE_WITH_CODE (MxButton, mx_button, MX_TYPE_BIN,
                         G_IMPLEMENT_INTERFACE (MX_TYPE_STYLABLE,
                                                mx_stylable_iface_init)
                         G_IMPLEMENT_INTERFACE (MX_TYPE_FOCUSABLE,
                                                mx_focusable_iface_init));

static void
mx_stylable_iface_init (MxStylableIface *iface)
{
  static gboolean is_initialized = FALSE;

  if (G_UNLIKELY (!is_initialized))
    {
      ClutterColor bg_color = { 0xcc, 0xcc, 0xcc, 0x00 };
      GParamSpec *pspec;

      is_initialized = TRUE;

      pspec = clutter_param_spec_color ("background-color",
                                        "Background Color",
                                        "The background color of an actor",
                                        &bg_color,
                                        G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_boxed ("x-mx-content-image",
                                   "Content Image",
                                   "Image used as the button",
                                   MX_TYPE_BORDER_IMAGE,
                                   G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_string ("x-mx-icon-name",
                                   "Icon name",
                                   "Named icon to place inside the button",
                                   NULL,
                                   G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);

      pspec = g_param_spec_int ("x-mx-icon-size",
                                "Icon size",
                                "Size to use for icon",
                                1, G_MAXINT, 48,
                                G_PARAM_READWRITE);
      mx_stylable_iface_install_property (iface, MX_TYPE_BUTTON, pspec);
    }
}

static MxFocusable*
mx_button_accept_focus (MxFocusable *focusable, MxFocusHint hint)
{
  /* FIXME: pretend hover is the same as focus for now */
  mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), "hover");
  MX_BUTTON (focusable)->priv->is_hover = TRUE;

  clutter_actor_grab_key_focus (CLUTTER_ACTOR (focusable));

  return focusable;
}

static MxFocusable*
mx_button_move_focus (MxFocusable *focusable,
                      MxDirection  direction,
                      MxFocusable *from)
{
  /* check if focus is being moved from us */
  if (focusable == from)
    {
      mx_stylable_set_style_pseudo_class (MX_STYLABLE (focusable), "");
      MX_BUTTON (focusable)->priv->is_hover = FALSE;
    }

  return NULL;
}

static void
mx_focusable_iface_init (MxFocusableIface *iface)
{
  iface->accept_focus = mx_button_accept_focus;
  iface->move_focus = mx_button_move_focus;
}

static void
mx_button_update_label_style (MxButton *button)
{
  ClutterActor *label;

  label = mx_bin_get_child ((MxBin*) button);

  /* check the child is really a label */
  if (!CLUTTER_IS_TEXT (label))
    return;

  mx_stylable_apply_clutter_text_attributes (MX_STYLABLE (button),
                                             CLUTTER_TEXT (label));
}

static void
mx_button_dispose_old_bg (MxButton *button)
{
  MxButtonPrivate *priv = button->priv;

  /* Temporary hack until a better solution comes about. Sometimes this
   * function gets called by the mx_animation_completed call below after the
   * object has been disposed.
   */
  g_return_if_fail (MX_IS_BUTTON (button));

  if (priv->content_image)
    {
      clutter_actor_unparent (priv->content_image);
      priv->content_image = NULL;
    }

  if (priv->old_bg)
    {
      if (priv->old_bg_parented)
        {
          clutter_actor_unparent (priv->old_bg);
          priv->old_bg_parented = FALSE;
        }
      g_object_unref (priv->old_bg);
{'fraction_non_alphanumeric': 0.08460347216562067, 'fraction_numerical': 0.006683511288613579, 'mean_word_length': 2.269384492406075, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 40, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1946597', 'n_tokens_mistral': 3910, 'n_tokens_neox': 3807, 'n_words': 952}