aboutsummaryrefslogblamecommitdiffstats
path: root/lib/egg/egg-action.c
blob: 4efcbac6be75d846ee24683af22ddd0a4fd81e23 (plain) (tree)
1
2
3
4
5

                          
                       
                    
 













                   
                










                                                             
                          

















































































































                                                                                                                                  







                                                                             



















                                                                                             

                                                 







                             
                            






                                  










                                                          








                                     

                                    




















































































                                                                     



                                                      






































                                                                 


                                                     
















































                                                                       





                                                                                  











                                                                                







































                                                                              
               

                                                                        








                                                                                   
























































































                                                                               










                                                                        



                                                                                 











                                                                          







                                                                    






























                                                                              



                                                                             
                                            


                                                                           
 
                                                             


































































































































                                                                            





























































                                                                                  
#include "egg-action.h"
#include "eggtoolbutton.h"
#include "eggtoolbar.h"
#include "eggintl.h"

enum {
  ACTIVATE,
  LAST_SIGNAL
};

enum {
  PROP_0,
  PROP_NAME,
  PROP_LABEL,
  PROP_SHORT_LABEL,
  PROP_TOOLTIP,
  PROP_STOCK_ID,
  PROP_SENSITIVE,
  PROP_VISIBLE,
  PROP_IMPORTANT
};

static void egg_action_init       (EggAction *action);
static void egg_action_class_init (EggActionClass *class);

static GQuark       accel_path_id  = 0;
static const gchar *accel_path_key = "EggAction::accel_path";

GType
egg_action_get_type (void)
{
  static GtkType type = 0;

  if (!type)
    {
      static const GTypeInfo type_info =
      {
        sizeof (EggActionClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) egg_action_class_init,
        (GClassFinalizeFunc) NULL,
        NULL,
        
        sizeof (EggAction),
        0, /* n_preallocs */
        (GInstanceInitFunc) egg_action_init,
      };

      type = g_type_register_static (G_TYPE_OBJECT,
                     "EggAction",
                     &type_info, 0);
    }
  return type;
}

static void egg_action_finalize     (GObject *object);
static void egg_action_set_property (GObject         *object,
                       guint            prop_id,
                       const GValue    *value,
                       GParamSpec      *pspec);
static void egg_action_get_property (GObject         *object,
                       guint            prop_id,
                       GValue          *value,
                       GParamSpec      *pspec);

static GtkWidget *create_menu_item    (EggAction *action);
static GtkWidget *create_tool_item    (EggAction *action);
static void       connect_proxy       (EggAction *action,
                       GtkWidget *proxy);
static void       disconnect_proxy    (EggAction *action,
                       GtkWidget *proxy);

static GObjectClass *parent_class = NULL;
static guint         action_signals[LAST_SIGNAL] = { 0 };


static void
egg_action_class_init (EggActionClass *class)
{
  GObjectClass *object_class;

  accel_path_id = g_quark_from_static_string(accel_path_key);

  parent_class = g_type_class_peek_parent (class);
  object_class = G_OBJECT_CLASS(class);

  object_class->finalize     = egg_action_finalize;
  object_class->set_property = egg_action_set_property;
  object_class->get_property = egg_action_get_property;

  class->activate = NULL;

  class->create_menu_item = create_menu_item;
  class->create_tool_item = create_tool_item;
  class->connect_proxy = connect_proxy;
  class->disconnect_proxy = disconnect_proxy;

  class->menu_item_type = GTK_TYPE_IMAGE_MENU_ITEM;
  class->toolbar_item_type = EGG_TYPE_TOOL_BUTTON;

  g_object_class_install_property (object_class,
                   PROP_NAME,
                   g_param_spec_string ("name",
                            _("Name"),
                            _("A unique name for the action."),
                            NULL,
                            G_PARAM_READWRITE |
                            G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class,
                   PROP_LABEL,
                   g_param_spec_string ("label",
                            _("Label"),
                            _("The label used for menu items and buttons that activate this action."),
                            NULL,
                            G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                   PROP_SHORT_LABEL,
                   g_param_spec_string ("short_label",
                            _("Short label"),
                            _("A shorter label that may be used on toolbar buttons."),
                            NULL,
                            G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                   PROP_TOOLTIP,
                   g_param_spec_string ("tooltip",
                            _("Tooltip"),
                            _("A tooltip for this action."),
                            NULL,
                            G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                   PROP_STOCK_ID,
                   g_param_spec_string ("stock_id",
                            _("Stock Icon"),
                            _("The stock icon displayed in widgets representing this action."),
                            NULL,
                            G_PARAM_READWRITE));
  g_object_class_install_property (object_class,
                   PROP_SENSITIVE,
                   g_param_spec_boolean ("sensitive",
                             _("Sensitive"),
                             _("Whether the action is enabled."),
                             TRUE,
                             G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                   PROP_IMPORTANT,
                   g_param_spec_boolean ("important",
                             _("Important"),
                             _("Important."),
                             FALSE,
                             G_PARAM_READWRITE));

  g_object_class_install_property (object_class,
                   PROP_VISIBLE,
                   g_param_spec_boolean ("visible",
                             _("Visible"),
                             _("Whether the action is visible."),
                             TRUE,
                             G_PARAM_READWRITE));

  action_signals[ACTIVATE] =
    g_signal_new ("activate",
          G_OBJECT_CLASS_TYPE (class),
          G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
          G_STRUCT_OFFSET (EggActionClass, activate),  NULL, NULL,
          g_cclosure_marshal_VOID__VOID,
          G_TYPE_NONE, 0);
}


static void
egg_action_init (EggAction *action)
{
  static GtkTooltips *egg_action_tooltips = NULL;

  action->name = NULL;
  action->label = NULL;
  action->short_label = NULL;
  action->tooltip = NULL;
  action->stock_id = NULL;

  action->sensitive = TRUE;
  action->visible = TRUE;
  action->important = FALSE;

  action->label_set = FALSE;
  action->short_label_set = FALSE;

  action->accel_quark = 0;

  action->proxies = NULL;

  if (egg_action_tooltips == NULL)
  {
    egg_action_tooltips = gtk_tooltips_new ();
    action->tooltips = g_object_ref (egg_action_tooltips);
    gtk_object_sink (GTK_OBJECT (egg_action_tooltips));
  }
  else
  {
    action->tooltips = g_object_ref (egg_action_tooltips);
  }
}

static void
egg_action_finalize (GObject *object)
{
  EggAction *action;

  action = EGG_ACTION (object);

  g_object_unref (action->tooltips);

  g_free (action->name);
  g_free (action->label);
  g_free (action->short_label);
  g_free (action->tooltip);
  g_free (action->stock_id);
}

static void
egg_action_set_property (GObject         *object,
               guint            prop_id,
               const GValue    *value,
               GParamSpec      *pspec)
{
  EggAction *action;

  action = EGG_ACTION (object);

  switch (prop_id)
    {
    case PROP_NAME:
      g_free (action->name);
      action->name = g_value_dup_string (value);
      break;
    case PROP_LABEL:
      g_free (action->label);
      action->label = g_value_dup_string (value);
      action->label_set = (action->label != NULL);
      /* if label is unset, then use the label from the stock item */
      if (!action->label_set && action->stock_id)
    {
      GtkStockItem stock_item;

      if (gtk_stock_lookup(action->stock_id, &stock_item))
        action->label = g_strdup(stock_item.label);
    }
      /* if short_label is unset, set short_label=label */
      if (!action->short_label_set)
    {
      g_free(action->short_label);
      action->short_label = g_strdup(action->label);
      g_object_notify(object, "short_label");
    }
      break;
    case PROP_SHORT_LABEL:
      g_free (action->short_label);
      action->short_label = g_value_dup_string (value);
      action->short_label_set = (action->short_label != NULL);
      /* if short_label is unset, then use the value of label */
      if (!action->short_label_set)
    {
      action->short_label = g_strdup(action->label);
    }
      break;
    case PROP_TOOLTIP:
      g_free (action->tooltip);
      action->tooltip = g_value_dup_string (value);
      break;
    case PROP_STOCK_ID:
      g_free (action->stock_id);
      action->stock_id = g_value_dup_string (value);
      /* update label and short_label if appropriate */
      if (!action->label_set)
    {
      GtkStockItem stock_item;

      g_free(action->label);
      if (gtk_stock_lookup(action->stock_id, &stock_item))
        action->label = g_strdup(stock_item.label);
      else
        action->label = NULL;
      g_object_notify(object, "label");
    }
      if (!action->short_label_set)
    {
      g_free(action->short_label);
      action->short_label = g_strdup(action->label);
      g_object_notify(object, "short_label");
    }
      break;
    case PROP_SENSITIVE:
      action->sensitive = g_value_get_boolean (value);
      break;
    case PROP_VISIBLE:
      action->visible = g_value_get_boolean (value);
      break;
    case PROP_IMPORTANT:
      action->important = g_value_get_boolean (value);
      g_object_notify(object, "important");
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
egg_action_get_property (GObject    *object,
             guint       prop_id,
             GValue     *value,
             GParamSpec *pspec)
{
  EggAction *action;

  action = EGG_ACTION (object);

  switch (prop_id)
    {
    case PROP_NAME:
      g_value_set_string (value, action->name);
      break;
    case PROP_LABEL:
      g_value_set_string (value, action->label);
      break;
    case PROP_SHORT_LABEL:
      g_value_set_string (value, action->short_label);
      break;
    case PROP_TOOLTIP:
      g_value_set_string (value, action->tooltip);
      break;
    case PROP_STOCK_ID:
      g_value_set_string (value, action->stock_id);
      break;
    case PROP_SENSITIVE:
      g_value_set_boolean (value, action->sensitive);
      break;
    case PROP_VISIBLE:
      g_value_set_boolean (value, action->visible);
      break;
    case PROP_IMPORTANT:
      g_value_set_boolean (value, action->important);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static GtkWidget *
create_menu_item (EggAction *action)
{
  GType menu_item_type;

  menu_item_type = EGG_ACTION_GET_CLASS (action)->menu_item_type;

  return g_object_new (menu_item_type, NULL);
}

static GtkWidget *
create_tool_item (EggAction *action)
{
  GType toolbar_item_type;

  toolbar_item_type = EGG_ACTION_GET_CLASS (action)->toolbar_item_type;

  return g_object_new (toolbar_item_type, NULL);
}

static void
egg_action_remove_proxy (GtkWidget *widget, EggAction *action)
{
  action->proxies = g_slist_remove (action->proxies, widget);
}

static void
egg_action_sync_property (EggAction *action, GParamSpec *pspec,
              GtkWidget *proxy)
{
  const gchar *property;
  GValue value = { 0, };

  property = g_param_spec_get_name (pspec);

  g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
  g_object_get_property (G_OBJECT (action), property, &value);

  g_object_set_property (G_OBJECT (proxy), property, &value);
  g_value_unset (&value);
}

static void
egg_action_sync_important (EggAction *action, GParamSpec *pspec, GtkWidget *proxy)
{
    egg_tool_item_set_is_important (EGG_TOOL_ITEM (proxy), action->important);
}

static void
egg_action_sync_tooltip (EggAction *action, GParamSpec *pspec, GtkWidget *proxy)
{
    if (action->tooltip != NULL)
    {
        egg_tool_item_set_tooltip (EGG_TOOL_ITEM (proxy),
                       action->tooltips,
                       action->tooltip,
                       NULL);
    }
}

static void
egg_action_sync_label (EggAction *action, GParamSpec *pspec, GtkWidget *proxy)
{
  GtkWidget *label = NULL;

  g_return_if_fail (GTK_IS_MENU_ITEM (proxy));
  label = GTK_BIN(proxy)->child;

  if (GTK_IS_LABEL (label))
    gtk_label_set_label (GTK_LABEL (label), action->label);
}

static void
egg_action_sync_short_label (EggAction *action, GParamSpec *pspec,
                 GtkWidget *proxy)
{
  GValue value = { 0, };

  g_value_init(&value, G_TYPE_STRING);
  g_object_get_property (G_OBJECT (action), "short_label", &value);

  g_object_set_property (G_OBJECT (proxy), "label", &value);
  g_value_unset (&value);
}

static void
egg_action_sync_stock_id (EggAction *action, GParamSpec *pspec,
              GtkWidget *proxy)
{
  GtkWidget *image = NULL;

  if (GTK_IS_IMAGE_MENU_ITEM (proxy))
    {
      image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy));

      if (GTK_IS_IMAGE (image))
    gtk_image_set_from_stock (GTK_IMAGE (image),
                  action->stock_id, GTK_ICON_SIZE_MENU);
    }
}

static gboolean
egg_action_create_menu_proxy (EggToolItem *tool_item, EggAction *action)
{
  GtkWidget *menu_item = egg_action_create_menu_item (action);

  g_object_ref (menu_item);
  gtk_object_sink (GTK_OBJECT (menu_item));
  
  egg_tool_item_set_proxy_menu_item (tool_item, "egg-action-menu-item", menu_item);
  g_object_unref (menu_item);

  return TRUE;
}

static void
connect_proxy (EggAction *action, GtkWidget *proxy)
{
  g_object_ref (action);
  g_object_set_data_full (G_OBJECT (proxy), "egg-action", action,
              g_object_unref);

  /* add this widget to the list of proxies */
  action->proxies = g_slist_prepend (action->proxies, proxy);
  g_signal_connect (proxy, "destroy",
            G_CALLBACK (egg_action_remove_proxy), action);

  g_signal_connect_object (action, "notify::sensitive",
               G_CALLBACK (egg_action_sync_property), proxy, 0);
  gtk_widget_set_sensitive (proxy, action->sensitive);

  g_signal_connect_object (action, "notify::visible",
               G_CALLBACK (egg_action_sync_property), proxy, 0);
  if (action->visible)
    gtk_widget_show (proxy);
  else
    gtk_widget_hide (proxy);

  if (GTK_IS_MENU_ITEM (proxy))
    {
      GtkWidget *label;
      /* menu item specific synchronisers ... */
      
      label = GTK_BIN (proxy)->child;

      /* make sure label is a label */
      if (label && !GTK_IS_LABEL (label))
    {
      gtk_container_remove (GTK_CONTAINER(proxy), label);
      label = NULL;
    }
      if (!label)
    {
      label = g_object_new (GTK_TYPE_ACCEL_LABEL,
                "use_underline", TRUE,
                "xalign", 0.0,
                "visible", TRUE,
                "parent", proxy,
                "accel_widget", proxy,
                NULL);
    }
      gtk_label_set_label (GTK_LABEL (label), action->label);
      g_signal_connect_object (action, "notify::label",
                   G_CALLBACK (egg_action_sync_label), proxy, 0);

      if (GTK_IS_IMAGE_MENU_ITEM (proxy))
    {
      GtkWidget *image;

      image = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (proxy));
      if (image && !GTK_IS_IMAGE(image))
        {
          gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy),NULL);
          image = NULL;
        }
      if (!image)
        {
          image = gtk_image_new_from_stock (NULL,
                        GTK_ICON_SIZE_MENU);
          gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy),
                         image);
          gtk_widget_show (image);
        }
      gtk_image_set_from_stock (GTK_IMAGE (image),
                    action->stock_id, GTK_ICON_SIZE_MENU);
      g_signal_connect_object (action, "notify::stock_id",
                   G_CALLBACK (egg_action_sync_stock_id),
                   proxy, 0);
    }

      if (action->accel_quark)
    {
      gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy),
                g_quark_to_string (action->accel_quark));
    }

      g_signal_connect_object (proxy, "activate",
                   G_CALLBACK (egg_action_activate), action,
                   G_CONNECT_SWAPPED);
    }
  else if (EGG_IS_TOOL_BUTTON (proxy))
    {
      /* toolbar button specific synchronisers ... */

      /* synchronise the label */
      g_object_set (G_OBJECT (proxy),
            "label", action->short_label,
            "use_underline", TRUE,
            NULL);
      g_signal_connect_object (action, "notify::short_label",
                   G_CALLBACK (egg_action_sync_short_label),
                   proxy, 0);

      egg_action_sync_important (action, NULL, proxy);
      g_signal_connect_object (action, "notify::important",
                   G_CALLBACK (egg_action_sync_important), proxy, 0);

      g_object_set (G_OBJECT (proxy), "stock_id", action->stock_id, NULL);
      g_signal_connect_object (action, "notify::stock_id",
            G_CALLBACK (egg_action_sync_property), proxy, 0);

      g_signal_connect_object (proxy, "create_menu_proxy",
                   G_CALLBACK (egg_action_create_menu_proxy),
                   action, 0);

      g_signal_connect_object (proxy, "clicked",
                   G_CALLBACK (egg_action_activate), action,
                   G_CONNECT_SWAPPED);
    }

    if (EGG_IS_TOOL_ITEM (proxy))
    {
      egg_action_sync_tooltip (action, NULL, proxy);
      g_signal_connect_object (action, "notify::tooltip",
                   G_CALLBACK (egg_action_sync_tooltip),
                   proxy, 0);
    }
}

static void
disconnect_proxy (EggAction *action, GtkWidget *proxy)
{
  static guint notify_id = 0;

  if (!notify_id)
    notify_id = g_signal_lookup ("notify", G_TYPE_OBJECT);

  g_object_set_data (G_OBJECT (proxy), "egg-action", NULL);

  /* remove proxy from list of proxies */
  g_signal_handlers_disconnect_by_func (proxy,
                    G_CALLBACK (egg_action_remove_proxy),
                    action);
  egg_action_remove_proxy (proxy, action);

  /* disconnect the activate handler */
  g_signal_handlers_disconnect_by_func (proxy,
                    G_CALLBACK (egg_action_activate),
                    action);

  /* disconnect handlers for notify::* signals */
  g_signal_handlers_disconnect_by_func (proxy,
                    G_CALLBACK (egg_action_sync_property),
                    action);

  g_signal_handlers_disconnect_by_func (action,
                G_CALLBACK (egg_action_sync_stock_id), proxy);

  g_signal_handlers_disconnect_by_func (proxy,
                    G_CALLBACK (egg_action_sync_tooltip),
                    action);            

  /* menu item specific synchronisers ... */
  g_signal_handlers_disconnect_by_func (action,
                    G_CALLBACK (egg_action_sync_label),
                    proxy);

  gtk_menu_item_set_accel_path (GTK_MENU_ITEM (proxy), NULL);

  /* toolbar button specific synchronisers ... */
  g_signal_handlers_disconnect_by_func (action,
            G_CALLBACK (egg_action_sync_short_label),
            proxy);
  g_signal_handlers_disconnect_by_func (proxy,
            G_CALLBACK (egg_action_create_menu_proxy),
            action);
}

/**
 * egg_action_activate:
 * @action: the action object
 *
 * Calling this function will emit the "activate" signal on the
 * specified action.  It gets called by the proxy widgets when they
 * get activated.
 *
 * It can also be used to manually activate an action.
 */
void
egg_action_activate (EggAction *action)
{
  g_signal_emit (action, action_signals[ACTIVATE], 0);
}

/**
 * egg_action_create_icon:
 * @action: the action object
 * @icon_size: the size of the icon that should be created.
 *
 * This function is intended for use by action implementations to
 * create icons displayed in the proxy widgets.
 *
 * Returns: a widget that displays the icon for this action.
 */
GtkWidget *
egg_action_create_icon (EggAction *action, GtkIconSize icon_size)
{
  if (action->stock_id)
    return gtk_image_new_from_stock (action->stock_id, icon_size);
  else
    return NULL;
}

/**
 * egg_action_create_menu_item:
 * @action: the action object
 *
 * This function creates a menu item widget that proxies for the given
 * action.
 *
 * Returns: a menu item connected to the action.
 */
GtkWidget *
egg_action_create_menu_item (EggAction *action)
{
  GtkWidget *menu_item;

  menu_item = (* EGG_ACTION_GET_CLASS (action)->create_menu_item) (action);

  (* EGG_ACTION_GET_CLASS (action)->connect_proxy) (action, menu_item);

  return menu_item;
}

/**
 * egg_action_create_tool_item:
 * @action: the action object
 *
 * This function creates a toolbar item widget that proxies for the
 * given action.
 *
 * Returns: a toolbar item connected to the action.
 */
GtkWidget *
egg_action_create_tool_item (EggAction *action)
{
  GtkWidget *button;

  button = (* EGG_ACTION_GET_CLASS (action)->create_tool_item) (action);

  (* EGG_ACTION_GET_CLASS (action)->connect_proxy) (action, button);

  return button;
}

/**
 * egg_action_connect_proxy:
 * @action: the action object
 * @proxy: the proxy widget
 *
 * This function connects a widget to an action object as a proxy.  It
 * will synchronise various properties of the action with the widget
 * (such as label text, icon, tooltip, etc), and attaches a callback
 * so that the action gets activated when the proxy widget does.
 *
 * If the widget is already connected to an action, it is disconnected
 * first.
 */
void
egg_action_connect_proxy (EggAction *action,
              GtkWidget *proxy)
{
  EggAction *prev_action;

  g_return_if_fail (EGG_IS_ACTION (action));
  g_return_if_fail (GTK_IS_WIDGET (proxy));

  prev_action = g_object_get_data (G_OBJECT (proxy), "egg-action");

  if (prev_action)
    {
      (* EGG_ACTION_GET_CLASS (action)->disconnect_proxy) (action, proxy);  
    }

  (* EGG_ACTION_GET_CLASS (action)->connect_proxy) (action, proxy);
}

/**
 * egg_action_disconnect_proxy:
 * @action: the action object
 * @proxy: the proxy widget
 *
 * This disconnects a proxy widget from an action.  It does not
 * destroy the widget, however.
 */
void
egg_action_disconnect_proxy (EggAction *action,
                 GtkWidget *proxy)
{
  g_return_if_fail (EGG_IS_ACTION (action));
  g_return_if_fail (GTK_IS_WIDGET (proxy));

  g_return_if_fail (g_object_get_data (G_OBJECT (proxy), "egg-action") != action);

  (* EGG_ACTION_GET_CLASS (action)->disconnect_proxy) (action, proxy);  
}

/**
 * egg_action_block_activate_from:
 * @action: the action object
 * @proxy: a proxy widget
 *
 * Calling this function disables calls to the egg_action_activate()
 * function by signals on the given proxy widget.  This is used to
 * break notification loops for things like check or radio actions.
 *
 * This function is intended for use by action implementations.
 */
void
egg_action_block_activate_from (EggAction *action, GtkWidget *proxy)
{
  g_return_if_fail (EGG_IS_ACTION (action));
  
  g_signal_handlers_block_by_func (proxy, G_CALLBACK (egg_action_activate),
                   action);
}

/**
 * egg_action_unblock_activate_from:
 * @action: the action object
 * @proxy: a proxy widget
 *
 * Calling this function re-enables calls to the egg_action_activate()
 * function by signals on the given proxy widget.  This undoes the
 * blocking done by egg_action_block_activate_from().
 *
 * This function is intended for use by action implementations.
 */
void
egg_action_unblock_activate_from (EggAction *action, GtkWidget *proxy)
{
  g_return_if_fail (EGG_IS_ACTION (action));

  g_signal_handlers_unblock_by_func (proxy, G_CALLBACK (egg_action_activate),
                     action);
}

/**
 * egg_action_set_accel_path:
 * @action: the action object
 * @accel_path: the accelerator path
 *
 * Sets the accel path for this action.  All proxy widgets associated
 * with the action will have this accel path, so that their
 * accelerators are consistent.
 */
void
egg_action_set_accel_path (EggAction *action, const gchar *accel_path)
{
  action->accel_quark = g_quark_from_string(accel_path);
}