glade-command.c 89.4 KB
Newer Older
1
/*
2
 * Copyright (C) 2002 Joaquín Cuenca Abela
3 4 5 6 7 8 9 10 11 12 13 14 15
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 18
 *
 * Authors:
19
 *   Joaquín Cuenca Abela <e98cuenc@yahoo.com>
20
 *   Archit Baweja <bighead@users.sourceforge.net>
21
 */
22 23 24
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
Paolo Borelli's avatar
Paolo Borelli committed
25

26 27 28 29 30 31 32 33
/**
 * SECTION:glade-command
 * @Short_Description: An event filter to implement the Undo/Redo stack.
 *
 * The Glade Command api allows us to view user actions as items and execute 
 * and undo those items; each #GladeProject has its own Undo/Redo stack.
 */

34 35
#include <gtk/gtk.h>
#include <string.h>
36
#include <glib/gi18n-lib.h>
37
#include "glade.h"
38 39 40
#include "glade-project.h"
#include "glade-xml-utils.h"
#include "glade-widget.h"
41
#include "glade-palette.h"
42 43
#include "glade-command.h"
#include "glade-property.h"
44
#include "glade-property-class.h"
45 46
#include "glade-debug.h"
#include "glade-placeholder.h"
47
#include "glade-clipboard.h"
48
#include "glade-signal.h"
49
#include "glade-app.h"
50
#include "glade-name-context.h"
Tristan Van Berkom's avatar
Tristan Van Berkom committed
51

52 53 54 55 56 57 58 59 60 61 62 63 64 65
struct _GladeCommandPrivate
{
  GladeProject *project; /* The project this command is created for */

  gchar *description; /* a string describing the command.
		       * It's used in the undo/redo menu entry.
		       */

  gint   group_id;    /* If this is part of a command group, this is
		       * the group id (id is needed only to ensure that
		       * consecutive groups dont get merged).
		       */
};

Tristan Van Berkom's avatar
Tristan Van Berkom committed
66 67 68 69 70 71
/* Concerning placeholders: we do not hold any reference to placeholders,
 * placeholders that are supplied by the backend are not reffed, placeholders
 * that are created by glade-command are temporarily owned by glade-command
 * untill they are added to a container; in which case it belongs to GTK+
 * and the backend (but I prefer to think of it as the backend).
 */
72 73 74 75 76 77 78 79 80 81
typedef struct
{
  GladeWidget *widget;
  GladeWidget *parent;
  GList *reffed;
  GladePlaceholder *placeholder;
  gboolean props_recorded;
  GList *pack_props;
  gchar *special_type;
  gulong handler_id;
82
} CommandData;
83

84 85
/* Group description used for the current group
 */
86
static gchar *gc_group_description = NULL;
87 88 89

/* Use an id to avoid grouping together consecutive groups
 */
90
static gint gc_group_id = 1;
91 92 93

/* Groups can be grouped together, push/pop must balance (duh!)
 */
94
static gint gc_group_depth = 0;
95

96

97
G_DEFINE_TYPE_WITH_PRIVATE (GladeCommand, glade_command, G_TYPE_OBJECT)
98 99

static void
100
glade_command_finalize (GObject *obj)
101
{
102 103
  GladeCommand *cmd = (GladeCommand *) obj;
  g_return_if_fail (cmd != NULL);
104

105 106
  if (cmd->priv->description)
    g_free (cmd->priv->description);
107

108
  /* Call the base class dtor */
109
  (*G_OBJECT_CLASS (glade_command_parent_class)->finalize) (obj);
110 111 112
}

static gboolean
113
glade_command_unifies_impl (GladeCommand *this_cmd, GladeCommand *other_cmd)
114
{
115
  return FALSE;
116 117 118
}

static void
119
glade_command_collapse_impl (GladeCommand *this_cmd, GladeCommand *other_cmd)
120
{
121
  g_return_if_reached ();
122 123
}

124 125 126
static void
glade_command_init (GladeCommand *command)
{
127
  command->priv = glade_command_get_instance_private (command);
128 129
}

130
static void
131
glade_command_class_init (GladeCommandClass *klass)
132
{
133
  GObjectClass *object_class;
134

135
  object_class = G_OBJECT_CLASS (klass);
136

137
  object_class->finalize = glade_command_finalize;
138

139 140 141 142
  klass->undo = NULL;
  klass->execute = NULL;
  klass->unifies = glade_command_unifies_impl;
  klass->collapse = glade_command_collapse_impl;
143 144
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/* Macros for defining the derived command types */
#define MAKE_TYPE(func, type, parent)			\
GType							\
func ## _get_type (void)				\
{							\
	static GType cmd_type = 0;			\
							\
	if (!cmd_type)					\
	{						\
		static const GTypeInfo info =		\
		{					\
			sizeof (type ## Class),		\
			(GBaseInitFunc) NULL,		\
			(GBaseFinalizeFunc) NULL,	\
			(GClassInitFunc) func ## _class_init,	\
			(GClassFinalizeFunc) NULL,	\
			NULL,				\
			sizeof (type),			\
			0,				\
			(GInstanceInitFunc) NULL	\
		};					\
							\
		cmd_type = g_type_register_static (parent, #type, &info, 0);	\
	}						\
							\
	return cmd_type;				\
}							\

173 174 175 176 177 178 179 180
#define GLADE_MAKE_COMMAND(type, func)					\
static gboolean								\
func ## _undo (GladeCommand *me);					\
static gboolean								\
func ## _execute (GladeCommand *me);					\
static void								\
func ## _finalize (GObject *object);					\
static gboolean								\
181
func ## _unifies (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
182
static void								\
183
func ## _collapse (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
184 185 186 187 188
static void								\
func ## _class_init (gpointer parent_tmp, gpointer notused)		\
{									\
	GladeCommandClass *parent = parent_tmp;				\
	GObjectClass* object_class;					\
189
	object_class = G_OBJECT_CLASS (parent);				\
190 191
	parent->undo =  func ## _undo;					\
	parent->execute =  func ## _execute;				\
192 193
	parent->unifies =  func ## _unifies;				\
	parent->collapse =  func ## _collapse;				\
194 195 196 197 198
	object_class->finalize = func ## _finalize;			\
}									\
typedef struct {							\
	GladeCommandClass cmd;						\
} type ## Class;							\
199
static MAKE_TYPE(func, type, GLADE_TYPE_COMMAND)
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218


G_CONST_RETURN gchar *
glade_command_description (GladeCommand *command)
{
  g_return_val_if_fail (GLADE_IS_COMMAND (command), NULL);

  return command->priv->description;
}

gint
glade_command_group_id (GladeCommand *command)
{
  g_return_val_if_fail (GLADE_IS_COMMAND (command), -1);

  return command->priv->group_id;
}


219 220 221 222 223 224 225 226
/**
 * glade_command_execute:
 * @command: A #GladeCommand
 *
 * Executes @command
 *
 * Returns: whether the command was successfully executed
 */
227
gboolean
228
glade_command_execute (GladeCommand *command)
229
{
230 231
  g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
  return GLADE_COMMAND_GET_CLASS (command)->execute (command);
232 233 234
}


235 236 237 238 239 240
/**
 * glade_command_undo:
 * @command: A #GladeCommand
 *
 * Undo the effects of @command
 *
241
 * Returns: whether the command was successfully reversed
242 243
 */
gboolean
244
glade_command_undo (GladeCommand *command)
245
{
246 247
  g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
  return GLADE_COMMAND_GET_CLASS (command)->undo (command);
248 249
}

250
/**
251 252 253 254 255 256
 * glade_command_unifies:
 * @command: A #GladeCommand
 * @other: another #GladeCommand
 *
 * Checks whether @command and @other can be unified
 * to make one single command.
257
 *
258
 * Returns: whether they can be unified.
259
 */
260
gboolean
261
glade_command_unifies (GladeCommand *command, GladeCommand *other)
262
{
263
  g_return_val_if_fail (command, FALSE);
264

265 266 267
  /* Cannot unify with a part of a command group.
   * Unify atomic commands only
   */
268
  if (command->priv->group_id != 0 || (other && other->priv->group_id != 0))
269
    return FALSE;
270

271
  return GLADE_COMMAND_GET_CLASS (command)->unifies (command, other);
272 273
}

274
/**
275 276 277
 * glade_command_collapse:
 * @command: A #GladeCommand
 * @other: another #GladeCommand
278
 *
279 280
 * Merges @other into @command, so that @command now
 * covers both commands and @other can be dispensed with.
281
 */
282
void
283
glade_command_collapse (GladeCommand *command, GladeCommand *other)
284
{
285 286
  g_return_if_fail (command);
  GLADE_COMMAND_GET_CLASS (command)->collapse (command, other);
287 288 289 290
}

/**
 * glade_command_push_group:
291
 * @fmt:         The collective desctiption of the command group.
292 293
 *               only the description of the first group on the 
 *               stack is used when embedding groups.
294
 * @...: args to the format string.
295 296 297 298
 *
 * Marks the begining of a group.
 */
void
299
glade_command_push_group (const gchar *fmt, ...)
300
{
301
  va_list args;
302

303
  g_return_if_fail (fmt);
304

305 306 307 308 309 310 311 312
  /* Only use the description for the first group.
   */
  if (gc_group_depth++ == 0)
    {
      va_start (args, fmt);
      gc_group_description = g_strdup_vprintf (fmt, args);
      va_end (args);
    }
313 314 315 316 317 318 319 320 321 322
}

/**
 * glade_command_pop_group:
 *
 * Mark the end of a command group.
 */
void
glade_command_pop_group (void)
{
323 324 325 326 327
  if (gc_group_depth-- == 1)
    {
      gc_group_description = (g_free (gc_group_description), NULL);
      gc_group_id++;
    }
328

329 330
  if (gc_group_depth < 0)
    g_critical ("Unbalanced group stack detected in %s\n", G_STRFUNC);
331
}
332

333 334 335
gint
glade_command_get_group_depth (void)
{
336
  return gc_group_depth;
337 338
}

339
static void
340
glade_command_check_group (GladeCommand *cmd)
341
{
342 343 344
  g_return_if_fail (GLADE_IS_COMMAND (cmd));
  if (gc_group_description)
    {
345 346 347
      cmd->priv->description =
          (g_free (cmd->priv->description), g_strdup (gc_group_description));
      cmd->priv->group_id = gc_group_id;
348
    }
349 350
}

351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 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 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 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

/****************************************************/
/*******  GLADE_COMMAND_PROPERTY_ENABLED      *******/
/****************************************************/
typedef struct
{
  GladeCommand parent;
  GladeProperty *property;
  gboolean old_enabled;
  gboolean new_enabled;
} GladeCommandPropertyEnabled;

/* standard macros */
GLADE_MAKE_COMMAND (GladeCommandPropertyEnabled, glade_command_property_enabled);
#define GLADE_COMMAND_PROPERTY_ENABLED_TYPE       (glade_command_property_enabled_get_type ())
#define GLADE_COMMAND_PROPERTY_ENABLED(o)	  (G_TYPE_CHECK_INSTANCE_CAST \
						   ((o), GLADE_COMMAND_PROPERTY_ENABLED_TYPE, \
						    GladeCommandPropertyEnabled))
#define GLADE_COMMAND_PROPERTY_ENABLED_CLASS(k)	  (G_TYPE_CHECK_CLASS_CAST \
						   ((k), GLADE_COMMAND_PROPERTY_ENABLED_TYPE, \
						    GladeCommandPropertyEnabledClass))
#define GLADE_IS_COMMAND_PROPERTY_ENABLED(o)	   (G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_PROPERTY_ENABLED_TYPE))
#define GLADE_IS_COMMAND_PROPERTY_ENABLED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_PROPERTY_ENABLED_TYPE))


static gboolean
glade_command_property_enabled_execute (GladeCommand *cmd)
{
  GladeCommandPropertyEnabled *me = GLADE_COMMAND_PROPERTY_ENABLED (cmd);

  glade_property_set_enabled (me->property, me->new_enabled);

  return TRUE;
}

static gboolean
glade_command_property_enabled_undo (GladeCommand *cmd)
{
  GladeCommandPropertyEnabled *me = GLADE_COMMAND_PROPERTY_ENABLED (cmd);

  glade_property_set_enabled (me->property, me->old_enabled);

  return TRUE;
}

static void
glade_command_property_enabled_finalize (GObject *obj)
{
  GladeCommandPropertyEnabled *me;

  g_return_if_fail (GLADE_IS_COMMAND_PROPERTY_ENABLED (obj));

  me = GLADE_COMMAND_PROPERTY_ENABLED (obj);

  g_object_unref (me->property);
  glade_command_finalize (obj);
}

static gboolean
glade_command_property_enabled_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
{
  GladeCommandPropertyEnabled *cmd1;
  GladeCommandPropertyEnabled *cmd2;

  if (!other_cmd)
    {
      if (GLADE_IS_COMMAND_PROPERTY_ENABLED (this_cmd))
        {
          cmd1 = (GladeCommandPropertyEnabled *) this_cmd;

	  return (cmd1->old_enabled == cmd1->new_enabled);
        }
      return FALSE;
    }

  if (GLADE_IS_COMMAND_PROPERTY_ENABLED (this_cmd) &&
      GLADE_IS_COMMAND_PROPERTY_ENABLED (other_cmd))
    {
      cmd1 = GLADE_COMMAND_PROPERTY_ENABLED (this_cmd);
      cmd2 = GLADE_COMMAND_PROPERTY_ENABLED (other_cmd);

      return (cmd1->property == cmd2->property);
    }

  return FALSE;
}

static void
glade_command_property_enabled_collapse (GladeCommand *this_cmd,
					 GladeCommand *other_cmd)
{
  GladeCommandPropertyEnabled *this = GLADE_COMMAND_PROPERTY_ENABLED (this_cmd);
  GladeCommandPropertyEnabled *other = GLADE_COMMAND_PROPERTY_ENABLED (other_cmd);
  GladeWidget *widget;
  GladePropertyClass *pclass;

  this->new_enabled = other->new_enabled;

  widget = glade_property_get_widget (this->property);
  pclass = glade_property_get_class (this->property);

  g_free (this_cmd->priv->description);
  if (this->new_enabled)
    this_cmd->priv->description =
      g_strdup_printf (_("Enabling property %s on widget %s"),
		       glade_property_class_get_name (pclass),
		       glade_widget_get_name (widget));
  else
    this_cmd->priv->description =
      g_strdup_printf (_("Disabling property %s on widget %s"),
		       glade_property_class_get_name (pclass),
		       glade_widget_get_name (widget));
}

/**
 * glade_command_set_property_enabled:
 * @property: An optional #GladeProperty
 * @enabled: Whether the property should be enabled
 *
 * Enables or disables @property.
 *
 * @property must be an optional property.
 */
void
glade_command_set_property_enabled (GladeProperty *property,
				    gboolean       enabled)
{
  GladeCommandPropertyEnabled *me;
  GladeCommand *cmd;
  GladeWidget *widget;
  GladePropertyClass *pclass;
  gboolean old_enabled;

  /* Sanity checks */
  g_return_if_fail (GLADE_IS_PROPERTY (property));

  widget = glade_property_get_widget (property);
  g_return_if_fail (GLADE_IS_WIDGET (widget));

  /* Only applies to optional properties */
  pclass = glade_property_get_class (property);
  g_return_if_fail (glade_property_class_optional (pclass));

  /* Fetch current state */
  old_enabled = glade_property_get_enabled (property);

  /* Avoid useless command */
  if (old_enabled == enabled)
    return;

  me = g_object_new (GLADE_COMMAND_PROPERTY_ENABLED_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
  cmd->priv->project = glade_widget_get_project (widget);

  me->property = g_object_ref (property);
  me->new_enabled = enabled;
  me->old_enabled = old_enabled;

  if (enabled)
    cmd->priv->description =
      g_strdup_printf (_("Enabling property %s on widget %s"),
		       glade_property_class_get_name (pclass),
		       glade_widget_get_name (widget));
  else
    cmd->priv->description =
      g_strdup_printf (_("Disabling property %s on widget %s"),
		       glade_property_class_get_name (pclass),
		       glade_widget_get_name (widget));

  glade_command_check_group (GLADE_COMMAND (me));

  if (glade_command_property_enabled_execute (GLADE_COMMAND (me)))
    glade_project_push_undo (cmd->priv->project, cmd);
  else
    g_object_unref (G_OBJECT (me));
}

528 529
/**************************************************/
/*******     GLADE_COMMAND_SET_PROPERTY     *******/
530 531
/**************************************************/

532 533
/* create a new GladeCommandSetProperty class.  Objects of this class will
 * encapsulate a "set property" operation */
534

535 536 537 538 539 540
typedef struct
{
  GladeCommand parent;
  gboolean set_once;
  gboolean undo;
  GList *sdata;
541
} GladeCommandSetProperty;
542

543
/* standard macros */
544 545 546 547
GLADE_MAKE_COMMAND (GladeCommandSetProperty, glade_command_set_property);
#define GLADE_COMMAND_SET_PROPERTY_TYPE		(glade_command_set_property_get_type ())
#define GLADE_COMMAND_SET_PROPERTY(o)	  	(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_PROPERTY_TYPE, GladeCommandSetProperty))
#define GLADE_COMMAND_SET_PROPERTY_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_PROPERTY_TYPE, GladeCommandSetPropertyClass))
548 549
#define GLADE_IS_COMMAND_SET_PROPERTY(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_PROPERTY_TYPE))
#define GLADE_IS_COMMAND_SET_PROPERTY_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_PROPERTY_TYPE))
550

551
/* Undo the last "set property command" */
552
static gboolean
553
glade_command_set_property_undo (GladeCommand *cmd)
554
{
555
  return glade_command_set_property_execute (cmd);
556 557
}

558
/*
559 560
 * Execute the set property command and revert it. IE, after the execution of 
 * this function cmd will point to the undo action
561 562
 */
static gboolean
563
glade_command_set_property_execute (GladeCommand *cmd)
564
{
565 566 567 568 569 570
  GladeCommandSetProperty *me = (GladeCommandSetProperty *) cmd;
  GList *l;
  gboolean success;
  gboolean retval = FALSE;

  g_return_val_if_fail (me != NULL, FALSE);
571

572 573
  if (me->set_once != FALSE)
    glade_property_push_superuser ();
574

575 576
  for (l = me->sdata; l; l = l->next)
    {
577 578 579 580
      GValue              new_value = { 0, };
      GCSetPropData      *sdata = l->data;
      GladePropertyClass *pclass = glade_property_get_class (sdata->property);
      GladeWidget        *widget = glade_property_get_widget (sdata->property);
581

582
      g_value_init (&new_value, G_VALUE_TYPE (sdata->new_value));
583

584 585 586 587
      if (me->undo)
        g_value_copy (sdata->old_value, &new_value);
      else
        g_value_copy (sdata->new_value, &new_value);
588

589 590 591 592
#ifdef GLADE_ENABLE_DEBUG
      if (glade_get_debug_flags () & GLADE_DEBUG_COMMANDS)
	{
	  gchar *str =
593
            glade_widget_adaptor_string_from_value
594
            (glade_property_class_get_adaptor (pclass), pclass, &new_value);
595

596 597 598 599
	  g_print ("Setting %s property of %s to %s (sumode: %d)\n",
		   glade_property_class_id (pclass),
		   glade_widget_get_name (widget),
		   str, glade_property_superuser ());
600

601 602
	  g_free (str);
	}
603 604
#endif

605 606 607
      /* Packing properties need to be refreshed here since
       * they are reset when they get added to containers.
       */
608
      if (glade_property_class_get_is_packing (pclass))
609 610
        {
          GladeProperty *tmp_prop;
611

612
          tmp_prop = glade_widget_get_pack_property (widget, glade_property_class_id (pclass));
613

614 615 616 617 618 619
          if (sdata->property != tmp_prop)
            {
              g_object_unref (sdata->property);
              sdata->property = g_object_ref (tmp_prop);
            }
        }
620

621
      success = glade_property_set_value (sdata->property, &new_value);
622
      retval  = retval || success;
623

624 625 626 627
      if (!me->set_once && success)
        {
          /* If some verify functions didnt pass on 
           * the first go.. we need to record the actual
628
           * properties here. XXX should be able to use glade_property_get_value() here
629
           */
630
          g_value_copy (glade_property_inline_value (sdata->property), sdata->new_value);
631
        }
632

633 634
      g_value_unset (&new_value);
    }
635

636 637
  if (me->set_once != FALSE)
    glade_property_pop_superuser ();
638

639 640 641 642
  me->set_once = TRUE;
  me->undo = !me->undo;

  return retval;
643 644 645
}

static void
646
glade_command_set_property_finalize (GObject *obj)
647
{
648 649 650 651
  GladeCommandSetProperty *me;
  GList *l;

  me = GLADE_COMMAND_SET_PROPERTY (obj);
652

653 654 655
  for (l = me->sdata; l; l = l->next)
    {
      GCSetPropData *sdata = l->data;
656

657 658
      if (sdata->property)
        g_object_unref (G_OBJECT (sdata->property));
659

660 661 662 663 664 665 666 667 668
      if (sdata->old_value)
        {
          if (G_VALUE_TYPE (sdata->old_value) != 0)
            g_value_unset (sdata->old_value);
          g_free (sdata->old_value);
        }
      if (G_VALUE_TYPE (sdata->new_value) != 0)
        g_value_unset (sdata->new_value);
      g_free (sdata->new_value);
669

670 671
    }
  glade_command_finalize (obj);
672 673 674
}

static gboolean
675 676
glade_command_set_property_unifies (GladeCommand *this_cmd,
                                    GladeCommand *other_cmd)
677 678
{
  GladeCommandSetProperty *cmd1, *cmd2;
679
  GladePropertyClass *pclass1, *pclass2;
680
  GCSetPropData *pdata1, *pdata2;
681
  GladeWidget *widget1, *widget2;
682 683 684 685 686 687 688 689 690 691
  GList *list, *l;

  if (!other_cmd)
    {
      if (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd))
        {
          cmd1 = (GladeCommandSetProperty *) this_cmd;

          for (list = cmd1->sdata; list; list = list->next)
            {
692 693
              pdata1  = list->data;
	      pclass1 = glade_property_get_class (pdata1->property);
694

695
              if (glade_property_class_compare (pclass1,
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
                                                pdata1->old_value,
                                                pdata1->new_value))
                return FALSE;
            }
          return TRUE;

        }
      return FALSE;
    }

  if (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
      GLADE_IS_COMMAND_SET_PROPERTY (other_cmd))
    {
      cmd1 = (GladeCommandSetProperty *) this_cmd;
      cmd2 = (GladeCommandSetProperty *) other_cmd;

      if (g_list_length (cmd1->sdata) != g_list_length (cmd2->sdata))
        return FALSE;

      for (list = cmd1->sdata; list; list = list->next)
        {
717 718 719 720
          pdata1  = list->data;
	  pclass1 = glade_property_get_class (pdata1->property);
	  widget1 = glade_property_get_widget (pdata1->property);

721 722
          for (l = cmd2->sdata; l; l = l->next)
            {
723 724 725
              pdata2  = l->data;
	      pclass2 = glade_property_get_class (pdata2->property);
	      widget2 = glade_property_get_widget (pdata2->property);
726

727 728
              if (widget1 == widget2 &&
                  glade_property_class_match (pclass1, pclass2))
729 730 731 732 733 734 735 736 737 738 739 740 741
                break;
            }

          /* If both lists are the same length, and one class type
           * is not found in the other list, then we cant unify.
           */
          if (l == NULL)
            return FALSE;
        }

      return TRUE;
    }
  return FALSE;
742 743 744
}

static void
745 746
glade_command_set_property_collapse (GladeCommand *this_cmd,
                                     GladeCommand *other_cmd)
747 748 749
{
  GladeCommandSetProperty *cmd1, *cmd2;
  GCSetPropData *pdata1, *pdata2;
750
  GladePropertyClass *pclass1, *pclass2;
751
  GList *list, *l;
752

753 754
  g_return_if_fail (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
                    GLADE_IS_COMMAND_SET_PROPERTY (other_cmd));
755

756 757 758 759 760 761
  cmd1 = (GladeCommandSetProperty *) this_cmd;
  cmd2 = (GladeCommandSetProperty *) other_cmd;


  for (list = cmd1->sdata; list; list = list->next)
    {
762 763 764
      pdata1  = list->data;
      pclass1 = glade_property_get_class (pdata1->property);

765 766
      for (l = cmd2->sdata; l; l = l->next)
        {
767 768
          pdata2  = l->data;
	  pclass2 = glade_property_get_class (pdata2->property);
769

770
          if (glade_property_class_match (pclass1, pclass2))
771 772 773 774 775 776 777 778
            {
              /* Merge the GCSetPropData structs manually here
               */
              g_value_copy (pdata2->new_value, pdata1->new_value);
              break;
            }
        }
    }
779

780 781
  /* Set the description
   */
782 783 784
  g_free (this_cmd->priv->description);
  this_cmd->priv->description = other_cmd->priv->description;
  other_cmd->priv->description = NULL;
785
}
786

787

788 789
#define MAX_UNDO_MENU_ITEM_VALUE_LEN	10
static gchar *
790
glade_command_set_property_description (GladeCommandSetProperty *me)
791 792 793 794
{
  GCSetPropData *sdata;
  gchar *description = NULL;
  gchar *value_name;
795 796
  GladePropertyClass *pclass;
  GladeWidget *widget;
797 798 799 800 801 802 803

  g_assert (me->sdata);

  if (g_list_length (me->sdata) > 1)
    description = g_strdup_printf (_("Setting multiple properties"));
  else
    {
804
      sdata  = me->sdata->data;
805 806
      pclass = glade_property_get_class (sdata->property);
      widget = glade_property_get_widget (sdata->property);
807
      value_name = glade_widget_adaptor_string_from_value
808
	(glade_property_class_get_adaptor (pclass), pclass, sdata->new_value);
809 810 811 812 813

      if (!value_name || strlen (value_name) > MAX_UNDO_MENU_ITEM_VALUE_LEN
          || strchr (value_name, '_'))
        {
          description = g_strdup_printf (_("Setting %s of %s"),
814
                                         glade_property_class_get_name (pclass),
815
                                         glade_widget_get_name (widget));
816 817 818 819
        }
      else
        {
          description = g_strdup_printf (_("Setting %s of %s to %s"),
820
                                         glade_property_class_get_name (pclass),
821
                                         glade_widget_get_name (widget),
822 823 824 825 826 827
                                         value_name);
        }
      g_free (value_name);
    }

  return description;
828 829
}

830 831 832 833 834
/**
 * glade_command_set_properties_list:
 *
 * @props (element-type GladeProperty): List of #GladeProperty
 */
835
void
836
glade_command_set_properties_list (GladeProject *project, GList *props)
837
{
838 839 840 841 842 843
  GladeCommandSetProperty *me;
  GladeCommand *cmd;
  GCSetPropData *sdata;
  GList *list;
  gboolean success;
  gboolean multiple;
844

845 846
  g_return_if_fail (GLADE_IS_PROJECT (project));
  g_return_if_fail (props);
847

848 849 850
  me = (GladeCommandSetProperty *)
      g_object_new (GLADE_COMMAND_SET_PROPERTY_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
851
  cmd->priv->project = project;
852

853 854 855 856 857 858
  /* Ref all props */
  for (list = props; list; list = list->next)
    {
      sdata = list->data;
      g_object_ref (G_OBJECT (sdata->property));
    }
859

860
  me->sdata = props;
861
  cmd->priv->description = glade_command_set_property_description (me);
862

863 864
  multiple = g_list_length (me->sdata) > 1;
  if (multiple)
865
    glade_command_push_group ("%s", cmd->priv->description);
866

867
  glade_command_check_group (GLADE_COMMAND (me));
868

869
  /* Push onto undo stack only if it executes successfully. */
870
  success = glade_command_set_property_execute (cmd);
871 872

  if (success)
873
    glade_project_push_undo (cmd->priv->project, cmd);
874 875 876 877 878
  else
    g_object_unref (G_OBJECT (me));

  if (multiple)
    glade_command_pop_group ();
879 880 881 882
}


void
883 884 885 886
glade_command_set_properties (GladeProperty *property,
                              const GValue *old_value,
                              const GValue *new_value,
                              ...)
887 888 889
{
  GCSetPropData *sdata;
  GladeProperty *prop;
890 891
  GladeWidget   *widget;
  GladeProject  *project;
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930
  GValue *ovalue, *nvalue;
  GList *list = NULL;
  va_list vl;

  g_return_if_fail (GLADE_IS_PROPERTY (property));

  /* Add first set */
  sdata = g_new (GCSetPropData, 1);
  sdata->property = property;
  sdata->old_value = g_new0 (GValue, 1);
  sdata->new_value = g_new0 (GValue, 1);
  g_value_init (sdata->old_value, G_VALUE_TYPE (old_value));
  g_value_init (sdata->new_value, G_VALUE_TYPE (new_value));
  g_value_copy (old_value, sdata->old_value);
  g_value_copy (new_value, sdata->new_value);
  list = g_list_prepend (list, sdata);

  va_start (vl, new_value);
  while ((prop = va_arg (vl, GladeProperty *)) != NULL)
    {
      ovalue = va_arg (vl, GValue *);
      nvalue = va_arg (vl, GValue *);

      g_assert (GLADE_IS_PROPERTY (prop));
      g_assert (G_IS_VALUE (ovalue));
      g_assert (G_IS_VALUE (nvalue));

      sdata = g_new (GCSetPropData, 1);
      sdata->property = g_object_ref (G_OBJECT (prop));
      sdata->old_value = g_new0 (GValue, 1);
      sdata->new_value = g_new0 (GValue, 1);
      g_value_init (sdata->old_value, G_VALUE_TYPE (ovalue));
      g_value_init (sdata->new_value, G_VALUE_TYPE (nvalue));
      g_value_copy (ovalue, sdata->old_value);
      g_value_copy (nvalue, sdata->new_value);
      list = g_list_prepend (list, sdata);
    }
  va_end (vl);

931 932 933
  widget  = glade_property_get_widget (property);
  project = glade_widget_get_project (widget);
  glade_command_set_properties_list (project, list);
934 935
}

936
void
937
glade_command_set_property_value (GladeProperty *property, const GValue *pvalue)
938
{
939

940 941 942 943
  /* Dont generate undo/redo items for unchanging property values.
   */
  if (glade_property_equals_value (property, pvalue))
    return;
944

945
  glade_command_set_properties (property, glade_property_inline_value (property), pvalue, NULL);
946 947
}

948
void
949
glade_command_set_property (GladeProperty * property, ...)
950
{
951 952 953 954
  GValue *value;
  va_list args;

  g_return_if_fail (GLADE_IS_PROPERTY (property));
955

956
  va_start (args, property);
957
  value = glade_property_class_make_gvalue_from_vl (glade_property_get_class (property), args);
958 959 960
  va_end (args);

  glade_command_set_property_value (property, value);
961 962
}

963 964 965 966 967 968
/**************************************************/
/*******       GLADE_COMMAND_SET_NAME       *******/
/**************************************************/

/* create a new GladeCommandSetName class.  Objects of this class will
 * encapsulate a "set name" operation */
969 970 971
typedef struct
{
  GladeCommand parent;
972

973 974 975
  GladeWidget *widget;
  gchar *old_name;
  gchar *name;
976 977 978 979 980 981 982
} GladeCommandSetName;

/* standard macros */
GLADE_MAKE_COMMAND (GladeCommandSetName, glade_command_set_name);
#define GLADE_COMMAND_SET_NAME_TYPE		(glade_command_set_name_get_type ())
#define GLADE_COMMAND_SET_NAME(o)	  	(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_NAME_TYPE, GladeCommandSetName))
#define GLADE_COMMAND_SET_NAME_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_NAME_TYPE, GladeCommandSetNameClass))
983 984
#define GLADE_IS_COMMAND_SET_NAME(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_NAME_TYPE))
#define GLADE_IS_COMMAND_SET_NAME_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_NAME_TYPE))
985 986 987

/* Undo the last "set name command" */
static gboolean
988
glade_command_set_name_undo (GladeCommand *cmd)
989
{
990
  return glade_command_set_name_execute (cmd);
991 992
}

993
/*
994 995 996 997
 * Execute the set name command and revert it.  Ie, after the execution of this
 * function cmd will point to the undo action
 */
static gboolean
998
glade_command_set_name_execute (GladeCommand *cmd)
999
{
1000 1001 1002 1003 1004 1005
  GladeCommandSetName *me = GLADE_COMMAND_SET_NAME (cmd);
  char *tmp;

  g_return_val_if_fail (me != NULL, TRUE);
  g_return_val_if_fail (me->widget != NULL, TRUE);
  g_return_val_if_fail (me->name != NULL, TRUE);
1006

1007
  glade_project_set_widget_name (cmd->priv->project, me->widget, me->name);
1008

1009 1010 1011
  tmp = me->old_name;
  me->old_name = me->name;
  me->name = tmp;
1012

1013
  return TRUE;
1014 1015 1016
}

static void
1017
glade_command_set_name_finalize (GObject *obj)
1018
{
1019
  GladeCommandSetName *me;
1020

1021
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (obj));
1022

1023
  me = GLADE_COMMAND_SET_NAME (obj);
1024

1025 1026
  g_free (me->old_name);
  g_free (me->name);
1027

1028
  glade_command_finalize (obj);
1029 1030 1031
}

static gboolean
1032
glade_command_set_name_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
1033
{
1034 1035
  GladeCommandSetName *cmd1;
  GladeCommandSetName *cmd2;
1036

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
  if (!other_cmd)
    {
      if (GLADE_IS_COMMAND_SET_NAME (this_cmd))
        {
          cmd1 = (GladeCommandSetName *) this_cmd;

	  return (g_strcmp0 (cmd1->old_name, cmd1->name) == 0);
        }
      return FALSE;
    }

1048 1049 1050 1051 1052
  if (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
      GLADE_IS_COMMAND_SET_NAME (other_cmd))
    {
      cmd1 = GLADE_COMMAND_SET_NAME (this_cmd);
      cmd2 = GLADE_COMMAND_SET_NAME (other_cmd);
1053

1054 1055
      return (cmd1->widget == cmd2->widget);
    }
1056

1057
  return FALSE;
1058 1059 1060
}

static void
1061 1062
glade_command_set_name_collapse (GladeCommand *this_cmd,
                                 GladeCommand *other_cmd)
1063
{
1064 1065
  GladeCommandSetName *nthis = GLADE_COMMAND_SET_NAME (this_cmd);
  GladeCommandSetName *nother = GLADE_COMMAND_SET_NAME (other_cmd);
1066

1067 1068
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
                    GLADE_IS_COMMAND_SET_NAME (other_cmd));
1069

1070 1071 1072
  g_free (nthis->old_name);
  nthis->old_name = nother->old_name;
  nother->old_name = NULL;
1073

1074 1075
  g_free (this_cmd->priv->description);
  this_cmd->priv->description =
1076
      g_strdup_printf (_("Renaming %s to %s"), nthis->name, nthis->old_name);
1077 1078 1079 1080
}

/* this function takes the ownership of name */
void
1081
glade_command_set_name (GladeWidget *widget, const gchar *name)
1082
{
1083 1084
  GladeCommandSetName *me;
  GladeCommand *cmd;
1085

1086
  g_return_if_fail (GLADE_IS_WIDGET (widget));
1087
  g_return_if_fail (name && name[0]);
1088

1089 1090
  /* Dont spam the queue with false name changes.
   */
1091
  if (!strcmp (glade_widget_get_name (widget), name))
1092
    return;
1093

1094 1095
  me = g_object_new (GLADE_COMMAND_SET_NAME_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
1096
  cmd->priv->project = glade_widget_get_project (widget);
1097

1098 1099
  me->widget = widget;
  me->name = g_strdup (name);
1100
  me->old_name = g_strdup (glade_widget_get_name (widget));
1101

1102
  cmd->priv->description =
1103
      g_strdup_printf (_("Renaming %s to %s"), me->old_name, me->name);
1104

1105
  glade_command_check_group (GLADE_COMMAND (me));
1106

1107
  if (glade_command_set_name_execute (GLADE_COMMAND (me)))
1108
    glade_project_push_undo (cmd->priv->project, cmd);
1109 1110
  else
    g_object_unref (G_OBJECT (me));
1111 1112
}

1113 1114 1115 1116 1117 1118 1119
/******************************************************************************
 * 
 * add/remove
 * 
 * These canonical commands add/remove a widget list to/from the project.
 * 
 *****************************************************************************/
1120

1121 1122 1123 1124 1125 1126
typedef struct
{
  GladeCommand parent;
  GList *widgets;
  gboolean add;
  gboolean from_clipboard;
1127
} GladeCommandAddRemove;
1128

1129 1130 1131 1132 1133 1134 1135

GLADE_MAKE_COMMAND (GladeCommandAddRemove, glade_command_add_remove);
#define GLADE_COMMAND_ADD_REMOVE_TYPE			(glade_command_add_remove_get_type ())
#define GLADE_COMMAND_ADD_REMOVE(o)	  			(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_ADD_REMOVE_TYPE, GladeCommandAddRemove))
#define GLADE_COMMAND_ADD_REMOVE_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_ADD_REMOVE_TYPE, GladeCommandAddRemoveClass))
#define GLADE_IS_COMMAND_ADD_REMOVE(o)			(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_ADD_REMOVE_TYPE))
#define GLADE_IS_COMMAND_ADD_REMOVE_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_ADD_REMOVE_TYPE))
1136

1137
static void
1138
glade_command_placeholder_destroyed (GtkWidget *object, CommandData *cdata)
1139
{
1140 1141 1142 1143 1144
  if (GTK_WIDGET (cdata->placeholder) == object)
    {
      cdata->placeholder = NULL;
      cdata->handler_id = 0;
    }
1145 1146
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
1147
static void
1148 1149
glade_command_placeholder_connect (CommandData *cdata,
                                   GladePlaceholder *placeholder)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1150
{
1151
  g_assert (cdata && cdata->placeholder == NULL);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1152

1153 1154 1155
  /* Something like a no-op with no placeholder */
  if ((cdata->placeholder = placeholder) == NULL)
    return;
1156

1157 1158 1159
  cdata->handler_id = g_signal_connect
      (placeholder, "destroy",
       G_CALLBACK (glade_command_placeholder_destroyed), cdata);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1160 1161
}

1162 1163 1164 1165 1166 1167
/**
 * get_all_parentless_reffed_widgetst:
 *
 * @props (element-type GladeWidget) : List of #GladeWidget
 * @return (element-type GladeWidget) : List of #GladeWidget
 */
1168
static GList *
1169
get_all_parentless_reffed_widgets (GList *reffed, GladeWidget *widget)
1170
{
1171 1172
  GList *children, *l, *list;
  GladeWidget *child;
1173

1174 1175
  if ((list = glade_widget_get_parentless_reffed_widgets (widget)) != NULL)
    reffed = g_list_concat (reffed, list);
1176

1177
  children = glade_widget_get_children (widget);
1178

1179 1180
  for (l = children; l; l = l->next)
    {
1181 1182
      child  = glade_widget_get_from_gobject (l->data);
      reffed = get_all_parentless_reffed_widgets (reffed, child);
1183
    }
1184

1185
  g_list_free (children);
1186

1187
  return reffed;
1188 1189
}

1190 1191
/**
 * glade_command_add:
1192
 * @widgets (element-type GladeWidget): a #Glist
1193 1194
 * @parent: a #GladeWidget
 * @placeholder: a #GladePlaceholder
1195
 * @pasting: whether we are pasting an existing widget or creating a new one.
1196 1197 1198 1199
 *
 * Performs an add command on all widgets in @widgets to @parent, possibly
 * replacing @placeholder (note toplevels dont need a parent; the active project
 * will be used when pasting toplevel objects).
1200 1201 1202
 * Pasted widgets will persist packing properties from thier cut/copy source
 * while newly added widgets will prefer packing defaults.
 *
1203
 */
1204
void
1205 1206 1207 1208 1209
glade_command_add (GList            *widgets,
                   GladeWidget      *parent,
                   GladePlaceholder *placeholder, 
		   GladeProject     *project,
		   gboolean          pasting)
1210 1211
{
  GladeCommandAddRemove *me;
1212
  GladeCommand *cmd;
1213 1214
  CommandData *cdata;
  GladeWidget *widget = NULL;
1215
  GladeWidgetAdaptor *adaptor;
1216 1217 1218 1219 1220 1221 1222
  GList *l, *list, *children, *placeholders = NULL;
  GtkWidget *child;

  g_return_if_fail (widgets && widgets->data);
  g_return_if_fail (parent == NULL || GLADE_IS_WIDGET (parent));

  me = g_object_new (GLADE_COMMAND_ADD_REMOVE_TYPE, NULL);
1223
  cmd = GLADE_COMMAND (me);
1224 1225 1226 1227 1228 1229 1230
  me->add = TRUE;
  me->from_clipboard = pasting;

  /* Things can go wrong in this function if the dataset is inacurate,
   * we make no real attempt here to recover, just g_critical() and
   * fix the bugs as they pop up.
   */
1231 1232 1233 1234
  widget  = GLADE_WIDGET (widgets->data);
  adaptor = glade_widget_get_adaptor (widget);

  if (placeholder && GWA_IS_TOPLEVEL (adaptor) == FALSE)
1235
    cmd->priv->project = glade_placeholder_get_project (placeholder);
1236
  else
1237
    cmd->priv->project = project;
1238

1239
  GLADE_COMMAND (me)->priv->description =
1240
      g_strdup_printf (_("Add %s"), g_list_length (widgets) == 1 ?
1241
                       glade_widget_get_name (widget) : _("multiple"));
1242 1243 1244 1245 1246

  for (list = widgets; list && list->data; list = list->next)
    {
      widget = list->data;
      cdata = g_new0 (CommandData, 1);
1247
      if (glade_widget_get_internal (widget))
1248 1249
        g_critical ("Internal widget in Add");

1250 1251
      adaptor = glade_widget_get_adaptor (widget);

1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
      /* Widget */
      cdata->widget = g_object_ref (G_OBJECT (widget));

      /* Parentless ref */
      if ((cdata->reffed =
           get_all_parentless_reffed_widgets (cdata->reffed, widget)) != NULL)
        g_list_foreach (cdata->reffed, (GFunc) g_object_ref, NULL);

      /* Parent */
      if (parent == NULL)
1262
	cdata->parent = glade_widget_get_parent (widget);
1263
      else if (placeholder && GWA_IS_TOPLEVEL (adaptor) == FALSE)
1264 1265 1266
	cdata->parent = glade_placeholder_get_parent (placeholder);
      else
	cdata->parent = parent;
1267 1268 1269 1270 1271 1272 1273 1274 1275

      /* Placeholder */
      if (placeholder != NULL && g_list_length (widgets) == 1)
        {
          glade_command_placeholder_connect (cdata, placeholder);
        }
      else if (cdata->parent &&
               glade_widget_placeholder_relation (cdata->parent, widget))
        {
1276 1277 1278
          if ((children =
               glade_widget_adaptor_get_children (glade_widget_get_adaptor (cdata->parent), 
                                                  glade_widget_get_object (cdata->parent))) != NULL)
1279 1280 1281 1282 1283
            {
              for (l = children; l && l->data; l = l->next)
                {
                  child = l->data;

1284 1285 1286
                  /* Find a placeholder for this child, ignore special child types */
                  if (GLADE_IS_PLACEHOLDER (child) &&
                      g_object_get_data (G_OBJECT (child), "special-child-type") == NULL &&
1287
                      g_list_find (placeholders, child) == NULL)
1288 1289
                    {
                      placeholders = g_list_append (placeholders, child);
1290
                      glade_command_placeholder_connect (cdata, GLADE_PLACEHOLDER (child));
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305
                      break;
                    }
                }
              g_list_free (children);
            }
        }

      me->widgets = g_list_prepend (me->widgets, cdata);
    }

  glade_command_check_group (GLADE_COMMAND (me));

  /*
   * Push it onto the undo stack only on success
   */
1306
  if (glade_command_add_remove_execute (cmd))
1307
    glade_project_push_undo (cmd->priv->project, cmd);
1308 1309 1310 1311 1312 1313
  else
    g_object_unref (G_OBJECT (me));

  if (placeholders)
    g_list_free (placeholders);

1314
}
1315 1316

static void
1317
glade_command_delete_prop_refs (GladeWidget *widget)
1318
{
1319
  GladeProperty *property;
1320
  GList         *refs, *l;
1321

1322 1323
  refs = glade_widget_list_prop_refs (widget);
  for (l = refs; l; l = l->next)
1324
    {
1325
      property = l->data;
1326 1327
      glade_command_set_property (property, NULL);
    }
1328
  g_list_free (refs);
1329 1330
}

1331
static void glade_command_remove (GList *widgets);
1332 1333

static void
1334
glade_command_remove_locked (GladeWidget *widget, GList *reffed)
1335
{
1336 1337
  GList list = { 0, }, *widgets, *l;
  GladeWidget *locked;
1338

1339
  widgets = glade_widget_list_locked_widgets (widget);
1340