gtkactiongroup.c 31.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*
 * GTK - The GIMP Toolkit
 * Copyright (C) 1998, 1999 Red Hat, Inc.
 * All rights reserved.
 *
 * This Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This Library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Author: James Henstridge <james@daa.com.au>
 *
 * Modified by the GTK+ Team and others 2003.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#include <config.h>

#include "gtkactiongroup.h"
34
#include "gtkstock.h"
35 36 37
#include "gtktoggleaction.h"
#include "gtkradioaction.h"
#include "gtkaccelmap.h"
38
#include "gtkmarshalers.h"
39 40 41 42 43 44
#include "gtkintl.h"

#define GTK_ACTION_GROUP_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ACTION_GROUP, GtkActionGroupPrivate))

struct _GtkActionGroupPrivate 
{
45
  gchar           *name;
46 47
  gboolean	   sensitive;
  gboolean	   visible;
48 49 50 51 52
  GHashTable      *actions;

  GtkTranslateFunc translate_func;
  gpointer         translate_data;
  GtkDestroyNotify translate_notify;   
53 54
};

55 56 57 58 59 60 61 62 63
enum 
{
  CONNECT_PROXY,
  DISCONNECT_PROXY,
  PRE_ACTIVATE,
  POST_ACTIVATE,
  LAST_SIGNAL
};

64 65 66
enum 
{
  PROP_0,
67 68 69
  PROP_NAME,
  PROP_SENSITIVE,
  PROP_VISIBLE
70 71
};

72 73 74
static void       gtk_action_group_init            (GtkActionGroup      *self);
static void       gtk_action_group_class_init      (GtkActionGroupClass *class);
static void       gtk_action_group_finalize        (GObject             *object);
75 76 77 78 79 80 81 82
static void       gtk_action_group_set_property    (GObject             *object,
						    guint                prop_id,
						    const GValue        *value,
						    GParamSpec          *pspec);
static void       gtk_action_group_get_property    (GObject             *object,
						    guint                prop_id,
						    GValue              *value,
						    GParamSpec          *pspec);
83 84 85
static GtkAction *gtk_action_group_real_get_action (GtkActionGroup      *self,
						    const gchar         *name);

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

GType
gtk_action_group_get_type (void)
{
  static GType type = 0;

  if (!type)
    {
      static const GTypeInfo type_info =
      {
        sizeof (GtkActionGroupClass),
	NULL,           /* base_init */
        NULL,           /* base_finalize */
        (GClassInitFunc) gtk_action_group_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GtkActionGroup),
        0, /* n_preallocs */
        (GInstanceInitFunc) gtk_action_group_init,
      };

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

  return type;
}

static GObjectClass *parent_class = NULL;
115
static guint         action_group_signals[LAST_SIGNAL] = { 0 };
116 117 118 119 120 121 122 123 124 125

static void
gtk_action_group_class_init (GtkActionGroupClass *klass)
{
  GObjectClass *gobject_class;

  gobject_class = G_OBJECT_CLASS (klass);
  parent_class = g_type_class_peek_parent (klass);

  gobject_class->finalize = gtk_action_group_finalize;
126 127
  gobject_class->set_property = gtk_action_group_set_property;
  gobject_class->get_property = gtk_action_group_get_property;
128 129
  klass->get_action = gtk_action_group_real_get_action;

130 131 132
  g_object_class_install_property (gobject_class,
				   PROP_NAME,
				   g_param_spec_string ("name",
133 134
							P_("Name"),
							P_("A name for the action group."),
135 136 137
							NULL,
							G_PARAM_READWRITE |
							G_PARAM_CONSTRUCT_ONLY));
138 139 140
  g_object_class_install_property (gobject_class,
				   PROP_SENSITIVE,
				   g_param_spec_boolean ("sensitive",
141 142
							 P_("Sensitive"),
							 P_("Whether the action group is enabled."),
143 144 145 146 147
							 TRUE,
							 G_PARAM_READWRITE));
  g_object_class_install_property (gobject_class,
				   PROP_VISIBLE,
				   g_param_spec_boolean ("visible",
148 149
							 P_("Visible"),
							 P_("Whether the action group is visible."),
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
							 TRUE,
							 G_PARAM_READWRITE));

  /**
   * GtkGroupAction::connect-proxy:
   * @action_group: the group
   * @action: the action
   * @proxy: the proxy
   *
   * The connect_proxy signal is emitted after connecting a proxy to 
   * an action in the group. Note that the proxy may have been connected 
   * to a different action before.
   *
   * This is intended for simple customizations for which a custom action
   * class would be too clumsy, e.g. showing tooltips for menuitems in the
   * statusbar.
   *
   * #GtkUIManager proxies the signal and provides global notification 
   * just before any action is connected to a proxy, which is probably more
   * convenient to use.
   *
   * Since: 2.4
   */
  action_group_signals[CONNECT_PROXY] =
    g_signal_new ("connect_proxy",
		  G_OBJECT_CLASS_TYPE (klass),
		  0, 0, NULL, NULL,
		  _gtk_marshal_VOID__OBJECT_OBJECT,
		  G_TYPE_NONE, 2,
		  GTK_TYPE_ACTION, GTK_TYPE_WIDGET);

  /**
   * GtkAction::disconnect-proxy:
   * @action_group: the group
   * @action: the action
   * @proxy: the proxy
   *
   * The disconnect_proxy signal is emitted after disconnecting a proxy 
   * from an action in the group. 
   *
   * #GtkUIManager proxies the signal and provides global notification 
   * just before any action is connected to a proxy, which is probably more
   * convenient to use.
   *
   * Since: 2.4
   */
  action_group_signals[DISCONNECT_PROXY] =
    g_signal_new ("disconnect_proxy",
		  G_OBJECT_CLASS_TYPE (klass),
		  0, 0, NULL, NULL,
		  _gtk_marshal_VOID__OBJECT_OBJECT,
		  G_TYPE_NONE, 2, 
		  GTK_TYPE_ACTION, GTK_TYPE_WIDGET);

  /**
   * GtkActionGroup::pre_activate:
   * @action_group: the group
   * @action: the action
   *
   * The pre_activate signal is emitted just before the @action in the
   * @action_group is activated
   *
   * This is intended for #GtkUIManager to proxy the signal and provide global
   * notification just before any action is activated.
   *
   * Since: 2.4
   */
  action_group_signals[PRE_ACTIVATE] =
    g_signal_new ("pre_activate",
		  G_OBJECT_CLASS_TYPE (klass),
		  0, 0, NULL, NULL,
		  _gtk_marshal_VOID__OBJECT,
		  G_TYPE_NONE, 1, 
		  GTK_TYPE_ACTION);

  /**
   * GtkActionGroup::post_activate:
   * @action_group: the group
   * @action: the action
   *
   * The post_activate signal is emitted just after the @action in the
   * @action_group is activated
   *
   * This is intended for #GtkUIManager to proxy the signal and provide global
   * notification just after any action is activated.
   *
   * Since: 2.4
   */
  action_group_signals[POST_ACTIVATE] =
    g_signal_new ("post_activate",
		  G_OBJECT_CLASS_TYPE (klass),
		  0, 0, NULL, NULL,
		  _gtk_marshal_VOID__OBJECT,
		  G_TYPE_NONE, 1, 
		  GTK_TYPE_ACTION);

246 247 248
  g_type_class_add_private (gobject_class, sizeof (GtkActionGroupPrivate));
}

Matthias Clasen's avatar
Matthias Clasen committed
249 250 251 252 253 254 255 256

static void 
remove_action (GtkAction *action) 
{
  g_object_set (action, "action_group", NULL, NULL);
  g_object_unref (action);
}

257 258 259 260 261
static void
gtk_action_group_init (GtkActionGroup *self)
{
  self->private_data = GTK_ACTION_GROUP_GET_PRIVATE (self);
  self->private_data->name = NULL;
262 263
  self->private_data->sensitive = TRUE;
  self->private_data->visible = TRUE;
264 265
  self->private_data->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
						       (GDestroyNotify) g_free,
Matthias Clasen's avatar
Matthias Clasen committed
266
						       (GDestroyNotify) remove_action);
267 268 269
  self->private_data->translate_func = NULL;
  self->private_data->translate_data = NULL;
  self->private_data->translate_notify = NULL;
270 271 272 273
}

/**
 * gtk_action_group_new:
Matthias Clasen's avatar
Matthias Clasen committed
274
 * @name: the name of the action group.
275
 *
Matthias Clasen's avatar
Matthias Clasen committed
276 277 278
 * Creates a new #GtkActionGroup object. The name of the action group
 * is used when associating <link linkend="Action-Accel">keybindings</link> 
 * with the actions.
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
 *
 * Returns: the new #GtkActionGroup
 *
 * Since: 2.4
 */
GtkActionGroup *
gtk_action_group_new (const gchar *name)
{
  GtkActionGroup *self;

  self = g_object_new (GTK_TYPE_ACTION_GROUP, NULL);
  self->private_data->name = g_strdup (name);

  return self;
}

static void
gtk_action_group_finalize (GObject *object)
{
  GtkActionGroup *self;

  self = GTK_ACTION_GROUP (object);

  g_free (self->private_data->name);
  self->private_data->name = NULL;

  g_hash_table_destroy (self->private_data->actions);
  self->private_data->actions = NULL;

308 309 310
  if (self->private_data->translate_notify)
    self->private_data->translate_notify (self->private_data->translate_data);

311 312 313 314
  if (parent_class->finalize)
    (* parent_class->finalize) (object);
}

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
static void
gtk_action_group_set_property (GObject         *object,
			       guint            prop_id,
			       const GValue    *value,
			       GParamSpec      *pspec)
{
  GtkActionGroup *self;
  gchar *tmp;
  
  self = GTK_ACTION_GROUP (object);

  switch (prop_id)
    {
    case PROP_NAME:
      tmp = self->private_data->name;
      self->private_data->name = g_value_dup_string (value);
      g_free (tmp);
      break;
333 334 335 336 337 338
    case PROP_SENSITIVE:
      gtk_action_group_set_sensitive (self, g_value_get_boolean (value));
      break;
    case PROP_VISIBLE:
      gtk_action_group_set_visible (self, g_value_get_boolean (value));
      break;
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_action_group_get_property (GObject    *object,
			       guint       prop_id,
			       GValue     *value,
			       GParamSpec *pspec)
{
  GtkActionGroup *self;
  
  self = GTK_ACTION_GROUP (object);

  switch (prop_id)
    {
    case PROP_NAME:
      g_value_set_string (value, self->private_data->name);
      break;
360 361 362 363 364 365
    case PROP_SENSITIVE:
      g_value_set_boolean (value, self->private_data->sensitive);
      break;
    case PROP_VISIBLE:
      g_value_set_boolean (value, self->private_data->visible);
      break;
366 367 368 369 370 371
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
static GtkAction *
gtk_action_group_real_get_action (GtkActionGroup *self,
				  const gchar    *action_name)
{
  return g_hash_table_lookup (self->private_data->actions, action_name);
}

/**
 * gtk_action_group_get_name:
 * @action_group: the action group
 *
 * Gets the name of the action group.
 *
 * Returns: the name of the action group.
 * 
 * Since: 2.4
 */
const gchar *
gtk_action_group_get_name (GtkActionGroup *action_group)
{
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);

  return action_group->private_data->name;
}

397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
/**
 * gtk_action_group_get_sensitive:
 * @action_group: the action group
 *
 * Returns %TRUE if the group is sensitive.  The constituent actions
 * can only be logically sensitive (see gtk_action_is_sensitive()) if
 * they are sensitive (see gtk_action_get_sensitive()) and their group
 * is sensitive.
 * 
 * Return value: %TRUE if the group is sensitive.
 *
 * Since: 2.4
 */
gboolean
gtk_action_group_get_sensitive (GtkActionGroup *action_group)
{
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);

  return action_group->private_data->sensitive;
}

static void
cb_set_action_sensitivity (const gchar *name, GtkAction *action)
{
  /* Minor optimization, the action_groups state only effects actions that are
   * themselves sensitive */
  if (gtk_action_get_sensitive (action))
    g_object_notify (G_OBJECT (action), "sensitive");
}

/**
 * gtk_action_group_set_sensitive:
 * @action_group: the action group
 * @sensitive: new sensitivity
 *
 * Changes the sensitivity of @action_group
 * 
 * Since: 2.4
 */
void
gtk_action_group_set_sensitive (GtkActionGroup *action_group, gboolean sensitive)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

  if (action_group->private_data->sensitive ^ sensitive)
    {
      action_group->private_data->sensitive = sensitive;
      g_hash_table_foreach (action_group->private_data->actions, 
			    (GHFunc) cb_set_action_sensitivity, NULL);
    }
}

/**
 * gtk_action_group_get_visible:
 * @action_group: the action group
 *
 * Returns %TRUE if the group is visible.  The constituent actions
 * can only be logically visible (see gtk_action_is_visible()) if
 * they are visible (see gtk_action_get_visible()) and their group
 * is visible.
 * 
 * Return value: %TRUE if the group is sensitive.
 * 
 * Since: 2.4
 */
gboolean
gtk_action_group_get_visible (GtkActionGroup *action_group)
{
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), FALSE);

  return action_group->private_data->visible;
}

static void
cb_set_action_visiblity (const gchar *name, GtkAction *action)
{
  /* Minor optimization, the action_groups state only effects actions that are
   * themselves sensitive */
  if (gtk_action_get_visible (action))
    g_object_notify (G_OBJECT (action), "visible");
}

/**
 * gtk_action_group_set_visible:
 * @action_group: the action group
 * @visible: new visiblity
 *
 * Changes the visible of @action_group.
 * 
 * Since: 2.4
 */
void
gtk_action_group_set_visible (GtkActionGroup *action_group, gboolean visible)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

  if (action_group->private_data->visible ^ visible)
    {
      action_group->private_data->visible = visible;
      g_hash_table_foreach (action_group->private_data->actions, 
			    (GHFunc) cb_set_action_visiblity, NULL);
    }
}

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
/**
 * gtk_action_group_get_action:
 * @action_group: the action group
 * @action_name: the name of the action
 *
 * Looks up an action in the action group by name.
 *
 * Returns: the action, or %NULL if no action by that name exists
 *
 * Since: 2.4
 */
GtkAction *
gtk_action_group_get_action (GtkActionGroup *action_group,
			     const gchar    *action_name)
{
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
  g_return_val_if_fail (GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action != NULL, NULL);

  return (* GTK_ACTION_GROUP_GET_CLASS (action_group)->get_action)
    (action_group, action_name);
}

/**
 * gtk_action_group_add_action:
 * @action_group: the action group
 * @action: an action
 *
528 529 530 531 532 533
 * Adds an action object to the action group. Note that this function
 * does not set up the accel path of the action, which can lead to problems
 * if a user tries to modify the accelerator of a menuitem associated with
 * the action. Therefore you must either set the accel path yourself with
 * gtk_action_set_accel_path(), or use 
 * <literal>gtk_action_group_add_action_with_accel (..., NULL)</literal>.
534 535 536 537 538 539 540 541 542 543 544 545 546 547
 *
 * Since: 2.4
 */
void
gtk_action_group_add_action (GtkActionGroup *action_group,
			     GtkAction      *action)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
  g_return_if_fail (GTK_IS_ACTION (action));
  g_return_if_fail (gtk_action_get_name (action) != NULL);

  g_hash_table_insert (action_group->private_data->actions, 
		       g_strdup (gtk_action_get_name (action)),
                       g_object_ref (action));
548
  g_object_set (G_OBJECT (action), "action_group", action_group, NULL);
549 550
}

551 552
/**
 * gtk_action_group_add_action_with_accel:
553 554
 * @action_group: the action group 
 * @action: the action to add 
555
 * @accelerator: the accelerator for the action, in
Matthias Clasen's avatar
Matthias Clasen committed
556 557
 *   the format understood by gtk_accelerator_parse(), or "" for no accelerator, or 
 *   %NULL to use the stock accelerator 
558 559 560
 *
 * Adds an action object to the action group and sets up the accelerator.
 *
561
 * If @accelerator is %NULL, attempts to use the accelerator associated 
Matthias Clasen's avatar
Matthias Clasen committed
562
 * with the stock_id of the action. 
563
 *
564 565 566
 * Accel paths are set to
 * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.
 *
567 568 569 570 571
 * Since: 2.4
 */
void
gtk_action_group_add_action_with_accel (GtkActionGroup *action_group,
					GtkAction *action,
572
					const gchar *accelerator)
573 574 575 576 577
{
  gchar *accel_path;
  guint  accel_key = 0;
  GdkModifierType accel_mods;
  GtkStockItem stock_item;
578 579
  gchar *name;
  gchar *stock_id;
580 581
  
  g_object_get (action, "name", &name, "stock_id", &stock_id, NULL);
582 583 584 585 586

  accel_path = g_strconcat ("<Actions>/",
			    action_group->private_data->name, "/", name, NULL);

  if (accelerator)
587
    {
Matthias Clasen's avatar
Matthias Clasen committed
588 589 590 591 592 593 594 595 596
      if (accelerator[0] == 0) 
	accel_key = 0;
      else
	{
	  gtk_accelerator_parse (accelerator, &accel_key, &accel_mods);
	  if (accel_key == 0)
	    g_warning ("Unable to parse accelerator '%s' for action '%s'",
		       accelerator, name);
	}
597
    }
598 599 600 601 602 603 604 605 606 607 608
  else if (stock_id && gtk_stock_lookup (stock_id, &stock_item))
    {
      accel_key = stock_item.keyval;
      accel_mods = stock_item.modifier;
    }

  if (accel_key)
    gtk_accel_map_add_entry (accel_path, accel_key, accel_mods);

  gtk_action_set_accel_path (action, accel_path);
  gtk_action_group_add_action (action_group, action);
609 610 611 612

  g_free (accel_path);
  g_free (stock_id);
  g_free (name);
613 614
}

615
/**
Matthias Clasen's avatar
Matthias Clasen committed
616
 * gtk_action_group_remove_action:
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
 * @action_group: the action group
 * @action: an action
 *
 * Removes an action object from the action group.
 *
 * Since: 2.4
 */
void
gtk_action_group_remove_action (GtkActionGroup *action_group,
				GtkAction      *action)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
  g_return_if_fail (GTK_IS_ACTION (action));
  g_return_if_fail (gtk_action_get_name (action) != NULL);

  /* extra protection to make sure action->name is valid */
  g_object_ref (action);
  g_hash_table_remove (action_group->private_data->actions, gtk_action_get_name (action));
  g_object_unref (action);
}

static void
add_single_action (gpointer key, 
		   gpointer value, 
		   gpointer user_data)
{
  GList **list = user_data;

  *list = g_list_prepend (*list, value);
}

/**
 * gtk_action_group_list_actions:
 * @action_group: the action group
 *
 * Lists the actions in the action group.
 *
 * Returns: an allocated list of the action objects in the action group
 * 
 * Since: 2.4
 */
GList *
gtk_action_group_list_actions (GtkActionGroup *action_group)
{
  GList *actions = NULL;
662
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), NULL);
663 664 665 666 667 668 669 670 671 672 673 674
  
  g_hash_table_foreach (action_group->private_data->actions, add_single_action, &actions);

  return g_list_reverse (actions);
}


/**
 * gtk_action_group_add_actions:
 * @action_group: the action group
 * @entries: an array of action descriptions
 * @n_entries: the number of entries
675
 * @user_data: data to pass to the action callbacks
676
 *
677 678
 * This is a convenience function to create a number of actions and add them 
 * to the action group.
Matthias Clasen's avatar
Matthias Clasen committed
679 680 681 682
 *
 * The "activate" signals of the actions are connected to the callbacks and 
 * their accel paths are set to 
 * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
683 684 685 686
 * 
 * Since: 2.4
 */
void
687 688 689 690 691 692 693 694 695 696
gtk_action_group_add_actions (GtkActionGroup *action_group,
			      GtkActionEntry *entries,
			      guint           n_entries,
			      gpointer        user_data)
{
  gtk_action_group_add_actions_full (action_group, 
				     entries, n_entries, 
				     user_data, NULL);
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
typedef struct _SharedData  SharedData;

struct _SharedData {
  guint          ref_count;
  gpointer       data;
  GDestroyNotify destroy;
};

static void
shared_data_unref (gpointer data)
{
  SharedData *shared_data = (SharedData *)data;

  shared_data->ref_count--;
  if (shared_data->ref_count == 0)
    {
      if (shared_data->destroy) 
	(*shared_data->destroy) (shared_data->data);
      
      g_free (shared_data);
    }
}

720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739

/**
 * gtk_action_group_add_actions_full:
 * @action_group: the action group
 * @entries: an array of action descriptions
 * @n_entries: the number of entries
 * @user_data: data to pass to the action callbacks
 * @destroy: destroy notification callback for @user_data
 *
 * This variant of gtk_action_group_add_actions() adds a #GDestroyNotify
 * callback for @user_data. 
 * 
 * Since: 2.4
 */
void
gtk_action_group_add_actions_full (GtkActionGroup *action_group,
				   GtkActionEntry *entries,
				   guint           n_entries,
				   gpointer        user_data,
				   GDestroyNotify  destroy)
740
{
741 742 743 744

  /* Keep this in sync with the other 
   * gtk_action_group_add_..._actions_full() functions.
   */
745
  guint i;
746
  SharedData *shared_data;
747

748
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
749

750 751 752 753 754
  shared_data = g_new0 (SharedData, 1);
  shared_data->ref_count = 1;
  shared_data->data = user_data;
  shared_data->destroy = destroy;

755 756 757
  for (i = 0; i < n_entries; i++)
    {
      GtkAction *action;
758 759
      const gchar *label;
      const gchar *tooltip;
760

761 762
      label = gtk_action_group_translate_string (action_group, entries[i].label);
      tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
763

764 765 766 767
      action = gtk_action_new (entries[i].name,
			       label,
			       tooltip,
			       entries[i].stock_id);
768 769

      if (entries[i].callback)
770 771 772 773 774 775 776
	{
	  GClosure *closure;

	  closure = g_cclosure_new (entries[i].callback, user_data, NULL);
	  g_closure_add_finalize_notifier (closure, shared_data, 
					   (GClosureNotify)shared_data_unref);
	  shared_data->ref_count++;
777

778 779 780
	  g_signal_connect_closure (action, "activate", closure, FALSE);
	}
	  
781 782 783
      gtk_action_group_add_action_with_accel (action_group, 
					      action,
					      entries[i].accelerator);
784 785
      g_object_unref (action);
    }
786 787

  shared_data_unref (shared_data);
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
}

/**
 * gtk_action_group_add_toggle_actions:
 * @action_group: the action group
 * @entries: an array of toggle action descriptions
 * @n_entries: the number of entries
 * @user_data: data to pass to the action callbacks
 *
 * This is a convenience function to create a number of toggle actions and add them 
 * to the action group.
 *
 * The "activate" signals of the actions are connected to the callbacks and 
 * their accel paths are set to 
 * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
 * 
 * Since: 2.4
 */
void
gtk_action_group_add_toggle_actions (GtkActionGroup       *action_group,
				     GtkToggleActionEntry *entries,
				     guint                 n_entries,
				     gpointer              user_data)
{
  gtk_action_group_add_toggle_actions_full (action_group, 
					    entries, n_entries, 
					    user_data, NULL);
}


/**
 * gtk_action_group_add_toggle_actions_full:
 * @action_group: the action group
 * @entries: an array of toggle action descriptions
 * @n_entries: the number of entries
 * @user_data: data to pass to the action callbacks
 * @destroy: destroy notification callback for @user_data
 *
 * This variant of gtk_action_group_add_toggle_actions() adds a 
 * #GDestroyNotify callback for @user_data. 
 * 
 * Since: 2.4
 */
void
gtk_action_group_add_toggle_actions_full (GtkActionGroup       *action_group,
					  GtkToggleActionEntry *entries,
					  guint                 n_entries,
					  gpointer              user_data,
					  GDestroyNotify        destroy)
{
838 839 840
  /* Keep this in sync with the other 
   * gtk_action_group_add_..._actions_full() functions.
   */
841
  guint i;
842
  SharedData *shared_data;
843 844 845

  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

846 847 848 849 850
  shared_data = g_new0 (SharedData, 1);
  shared_data->ref_count = 1;
  shared_data->data = user_data;
  shared_data->destroy = destroy;

851 852
  for (i = 0; i < n_entries; i++)
    {
853
      GtkToggleAction *action;
854 855
      const gchar *label;
      const gchar *tooltip;
856

857 858
      label = gtk_action_group_translate_string (action_group, entries[i].label);
      tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
859

860 861 862 863
      action = gtk_toggle_action_new (entries[i].name,
				      label,
				      tooltip,
				      entries[i].stock_id);
864

865
      gtk_toggle_action_set_active (action, entries[i].is_active);
866

867
      if (entries[i].callback)
868 869 870 871 872 873 874
	{
	  GClosure *closure;

	  closure = g_cclosure_new (entries[i].callback, user_data, NULL);
	  g_closure_add_finalize_notifier (closure, shared_data, 
					   (GClosureNotify)shared_data_unref);
	  shared_data->ref_count++;
875

876 877 878
	  g_signal_connect_closure (action, "activate", closure, FALSE);
	}
	  
879
      gtk_action_group_add_action_with_accel (action_group, 
880
					      GTK_ACTION (action),
881
					      entries[i].accelerator);
882 883
      g_object_unref (action);
    }
884 885

    shared_data_unref (shared_data);
886 887 888 889 890 891 892
}

/**
 * gtk_action_group_add_radio_actions:
 * @action_group: the action group
 * @entries: an array of radio action descriptions
 * @n_entries: the number of entries
893 894
 * @value: the value of the action to activate initially, or -1 if
 *   no action should be activated
895 896 897
 * @on_change: the callback to connect to the changed signal
 * @user_data: data to pass to the action callbacks
 * 
Matthias Clasen's avatar
Matthias Clasen committed
898 899 900
 * This is a convenience routine to create a group of radio actions and
 * add them to the action group. 
 *
901 902
 * The "changed" signal of the first radio action is connected to the 
 * @on_change callback and the accel paths of the actions are set to 
Matthias Clasen's avatar
Matthias Clasen committed
903
 * <literal>&lt;Actions&gt;/<replaceable>group-name</replaceable>/<replaceable>action-name</replaceable></literal>.  
904 905 906 907 908 909 910
 * 
 * Since: 2.4
 **/
void            
gtk_action_group_add_radio_actions (GtkActionGroup      *action_group,
				    GtkRadioActionEntry *entries,
				    guint                n_entries,
911
				    gint                 value,
912 913 914 915 916
				    GCallback            on_change,
				    gpointer             user_data)
{
  gtk_action_group_add_radio_actions_full (action_group, 
					   entries, n_entries, 
917
					   value,
918 919 920
					   on_change, user_data, NULL);
}

Matthias Clasen's avatar
Matthias Clasen committed
921 922 923 924 925
/**
 * gtk_action_group_add_radio_actions_full:
 * @action_group: the action group
 * @entries: an array of radio action descriptions
 * @n_entries: the number of entries
926 927
 * @value: the value of the action to activate initially, or -1 if
 *   no action should be activated
Matthias Clasen's avatar
Matthias Clasen committed
928 929 930 931 932 933 934 935 936
 * @on_change: the callback to connect to the changed signal
 * @user_data: data to pass to the action callbacks
 * @destroy: destroy notification callback for @user_data
 *
 * This variant of gtk_action_group_add_radio_actions() adds a 
 * #GDestroyNotify callback for @user_data. 
 * 
 * Since: 2.4
 **/
937 938 939 940
void            
gtk_action_group_add_radio_actions_full (GtkActionGroup      *action_group,
					 GtkRadioActionEntry *entries,
					 guint                n_entries,
941
					 gint                 value,
942 943 944 945
					 GCallback            on_change,
					 gpointer             user_data,
					 GDestroyNotify       destroy)
{
946 947 948
  /* Keep this in sync with the other 
   * gtk_action_group_add_..._actions_full() functions.
   */
949
  guint i;
950
  GSList *group = NULL;
951
  GtkRadioAction *first_action = NULL;
952 953 954 955 956

  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

  for (i = 0; i < n_entries; i++)
    {
957
      GtkRadioAction *action;
958 959
      const gchar *label;
      const gchar *tooltip; 
960

961 962
      label = gtk_action_group_translate_string (action_group, entries[i].label);
      tooltip = gtk_action_group_translate_string (action_group, entries[i].tooltip);
963

964 965 966 967
      action = gtk_radio_action_new (entries[i].name,
				     label,
				     tooltip,
				     entries[i].stock_id,
968
				     entries[i].value);
969

970
      if (i == 0) 
971 972
	first_action = action;

973 974
      gtk_radio_action_set_group (action, group);
      group = gtk_radio_action_get_group (action);
975

976
      if (value == entries[i].value)
977
	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
978

979
      gtk_action_group_add_action_with_accel (action_group, 
980
					      GTK_ACTION (action),
981
					      entries[i].accelerator);
982 983
      g_object_unref (action);
    }
984

985
  if (on_change && first_action)
986 987 988
    g_signal_connect_data (first_action, "changed",
			   on_change, user_data, 
			   (GClosureNotify)destroy, 0);
989
}
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035

/**
 * gtk_action_group_set_translate_func:
 * @action_group: a #GtkActionGroup
 * @func: a #GtkTranslateFunc
 * @data: data to be passed to @func and @notify
 * @notify: a #GtkDestroyNotify function to be called when @action_group is 
 *   destroyed and when the translation function is changed again
 * 
 * Sets a function to be used for translating the @label and @tooltip of 
 * #GtkActionGroupEntry<!-- -->s added by gtk_action_group_add_actions().
 *
 * If you're using gettext(), it is enough to set the translation domain
 * with gtk_action_group_set_translation_domain().
 *
 * Since: 2.4 
 **/
void
gtk_action_group_set_translate_func (GtkActionGroup      *action_group,
				     GtkTranslateFunc     func,
				     gpointer             data,
				     GtkDestroyNotify     notify)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));
  
  if (action_group->private_data->translate_notify)
    action_group->private_data->translate_notify (action_group->private_data->translate_data);
      
  action_group->private_data->translate_func = func;
  action_group->private_data->translate_data = data;
  action_group->private_data->translate_notify = notify;
}

static gchar *
dgettext_swapped (const gchar *msgid, 
		  const gchar *domainname)
{
  return dgettext (domainname, msgid);
}

/**
 * gtk_action_group_set_translation_domain:
 * @action_group: a #GtkActionGroup
 * @domain: the translation domain to use for dgettext() calls
 * 
 * Sets the translation domain and uses dgettext() for translating the 
1036
 * @label and @tooltip of #GtkActionEntry<!-- -->s added by 
1037 1038 1039 1040
 * gtk_action_group_add_actions(). Note that GTK+ expects all strings
 * to be encoded in UTF-8, therefore the translation domain must have
 * its codeset set to UTF-8, see bind_textdomain_codeset() in the 
 * gettext() documentation. 
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
 *
 * If you're not using gettext() for localization, see 
 * gtk_action_group_set_translate_func().
 *
 * Since: 2.4
 **/
void 
gtk_action_group_set_translation_domain (GtkActionGroup *action_group,
					 const gchar    *domain)
{
  g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

  gtk_action_group_set_translate_func (action_group, 
1054
				       (GtkTranslateFunc)dgettext_swapped,
1055 1056 1057
				       g_strdup (domain),
				       g_free);
} 
1058

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
/**
 * gtk_action_group_translate_string:
 * @action_group: a #GtkActionGroup
 * @string: a string
 *
 * Translates a string using the specified translate_func(). This
 * is mainly intended for language bindings. 
 *
 * Returns: the translation of @string
 *
 * Since: 2.6
 **/
gchar *
gtk_action_group_translate_string (GtkActionGroup *action_group,
				   const gchar    *string)
{
  GtkTranslateFunc translate_func;
  gpointer translate_data;

1078
  g_return_val_if_fail (GTK_IS_ACTION_GROUP (action_group), string);
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088

  translate_func = action_group->private_data->translate_func;
  translate_data = action_group->private_data->translate_data;

  if (translate_func)
    return translate_func (string, translate_data);
  else
    return string;
}

1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
/* Protected for use by GtkAction */
void
_gtk_action_group_emit_connect_proxy  (GtkActionGroup *action_group,
                                       GtkAction      *action,
                                       GtkWidget      *proxy)
{
  g_signal_emit (action_group, action_group_signals[CONNECT_PROXY], 0, 
                 action, proxy);
}

void
_gtk_action_group_emit_disconnect_proxy  (GtkActionGroup *action_group,
                                          GtkAction      *action,
                                          GtkWidget      *proxy)
{
  g_signal_emit (action_group, action_group_signals[DISCONNECT_PROXY], 0, 
                 action, proxy);
}

void
_gtk_action_group_emit_pre_activate  (GtkActionGroup *action_group,
				      GtkAction      *action)
{
  g_signal_emit (action_group, action_group_signals[PRE_ACTIVATE], 0, action);
}

void
_gtk_action_group_emit_post_activate (GtkActionGroup *action_group,
				      GtkAction *action)
{
  g_signal_emit (action_group, action_group_signals[POST_ACTIVATE], 0, action);
}