glade-command.c 66.8 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"
Paolo Borelli's avatar
Paolo Borelli committed
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 (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
127
128
129
130
131
static void
glade_command_init (GladeCommand *command)
{
  command->priv = 
    G_TYPE_INSTANCE_GET_PRIVATE ((command), GLADE_TYPE_COMMAND,
				 GladeCommandPrivate);
}

132
static void
133
glade_command_class_init (GladeCommandClass * klass)
134
{
135
  GObjectClass *object_class;
136

137
  object_class = G_OBJECT_CLASS (klass);
138

139
  object_class->finalize = glade_command_finalize;
140

141
142
143
144
  klass->undo = NULL;
  klass->execute = NULL;
  klass->unifies = glade_command_unifies_impl;
  klass->collapse = glade_command_collapse_impl;
145
146

  g_type_class_add_private (klass, sizeof (GladeCommandPrivate));
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
173
174
175
176
/* 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;				\
}							\

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


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;
}


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


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

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

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

275
  return GLADE_COMMAND_GET_CLASS (command)->unifies (command, other);
276
277
}

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

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

307
  g_return_if_fail (fmt);
308

309
310
311
312
313
314
315
316
  /* 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);
    }
317
318
319
320
321
322
323
324
325
326
}

/**
 * glade_command_pop_group:
 *
 * Mark the end of a command group.
 */
void
glade_command_pop_group (void)
{
327
328
329
330
331
  if (gc_group_depth-- == 1)
    {
      gc_group_description = (g_free (gc_group_description), NULL);
      gc_group_id++;
    }
332

333
334
  if (gc_group_depth < 0)
    g_critical ("Unbalanced group stack detected in %s\n", G_STRFUNC);
335
}
336

337
338
339
gint
glade_command_get_group_depth (void)
{
340
  return gc_group_depth;
341
342
}

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

355
356
/**************************************************/
/*******     GLADE_COMMAND_SET_PROPERTY     *******/
357
358
/**************************************************/

359
360
/* create a new GladeCommandSetProperty class.  Objects of this class will
 * encapsulate a "set property" operation */
361

362
363
364
365
366
367
typedef struct
{
  GladeCommand parent;
  gboolean set_once;
  gboolean undo;
  GList *sdata;
368
} GladeCommandSetProperty;
369

370
/* standard macros */
371
372
373
374
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))
375
376
#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))
377

378
/* Undo the last "set property command" */
379
static gboolean
380
glade_command_set_property_undo (GladeCommand * cmd)
381
{
382
  return glade_command_set_property_execute (cmd);
383
384
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
385
/*
386
387
 * Execute the set property command and revert it. IE, after the execution of 
 * this function cmd will point to the undo action
388
389
 */
static gboolean
390
glade_command_set_property_execute (GladeCommand * cmd)
391
{
392
393
394
395
396
397
  GladeCommandSetProperty *me = (GladeCommandSetProperty *) cmd;
  GList *l;
  gboolean success;
  gboolean retval = FALSE;

  g_return_val_if_fail (me != NULL, FALSE);
398

399
400
  if (me->set_once != FALSE)
    glade_property_push_superuser ();
401

402
403
  for (l = me->sdata; l; l = l->next)
    {
404
405
406
407
      GValue              new_value = { 0, };
      GCSetPropData      *sdata = l->data;
      GladePropertyClass *pclass = glade_property_get_class (sdata->property);
      GladeWidget        *widget = glade_property_get_widget (sdata->property);
408

409
      g_value_init (&new_value, G_VALUE_TYPE (sdata->new_value));
410

411
412
413
414
      if (me->undo)
        g_value_copy (sdata->old_value, &new_value);
      else
        g_value_copy (sdata->new_value, &new_value);
415

416
#if 0
417
418
419
      {
        gchar *str =
            glade_widget_adaptor_string_from_value
420
            (GLADE_WIDGET_ADAPTOR (pclass->handle), pclass, &new_value);
421
422

        g_print ("Setting %s property of %s to %s (sumode: %d)\n",
423
424
                 pclass->id,
                 glade_widget_get_name (widget),
425
426
427
428
                 str, glade_property_superuser ());

        g_free (str);
      }
429
430
#endif

431
432
433
      /* Packing properties need to be refreshed here since
       * they are reset when they get added to containers.
       */
434
      if (glade_property_class_get_is_packing (pclass))
435
436
        {
          GladeProperty *tmp_prop;
437

438
          tmp_prop = glade_widget_get_pack_property (widget, glade_property_class_id (pclass));
439

440
441
442
443
444
445
          if (sdata->property != tmp_prop)
            {
              g_object_unref (sdata->property);
              sdata->property = g_object_ref (tmp_prop);
            }
        }
446

447
      success = glade_property_set_value (sdata->property, &new_value);
448
      retval  = retval || success;
449

450
451
452
453
      if (!me->set_once && success)
        {
          /* If some verify functions didnt pass on 
           * the first go.. we need to record the actual
454
           * properties here. XXX should be able to use glade_property_get_value() here
455
           */
456
          g_value_copy (glade_property_inline_value (sdata->property), sdata->new_value);
457
        }
458

459
460
      g_value_unset (&new_value);
    }
461

462
463
  if (me->set_once != FALSE)
    glade_property_pop_superuser ();
464

465
466
467
468
  me->set_once = TRUE;
  me->undo = !me->undo;

  return retval;
469
470
471
}

static void
472
glade_command_set_property_finalize (GObject * obj)
473
{
474
475
476
477
  GladeCommandSetProperty *me;
  GList *l;

  me = GLADE_COMMAND_SET_PROPERTY (obj);
478

479
480
481
  for (l = me->sdata; l; l = l->next)
    {
      GCSetPropData *sdata = l->data;
482

483
484
      if (sdata->property)
        g_object_unref (G_OBJECT (sdata->property));
485

486
487
488
489
490
491
492
493
494
      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);
495

496
497
    }
  glade_command_finalize (obj);
498
499
500
}

static gboolean
501
502
503
504
glade_command_set_property_unifies (GladeCommand * this_cmd,
                                    GladeCommand * other_cmd)
{
  GladeCommandSetProperty *cmd1, *cmd2;
505
  GladePropertyClass *pclass1, *pclass2;
506
  GCSetPropData *pdata1, *pdata2;
507
  GladeWidget *widget1, *widget2;
508
509
510
511
512
513
514
515
516
517
  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)
            {
518
519
              pdata1  = list->data;
	      pclass1 = glade_property_get_class (pdata1->property);
520

521
              if (glade_property_class_compare (pclass1,
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
                                                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)
        {
543
544
545
546
          pdata1  = list->data;
	  pclass1 = glade_property_get_class (pdata1->property);
	  widget1 = glade_property_get_widget (pdata1->property);

547
548
          for (l = cmd2->sdata; l; l = l->next)
            {
549
550
551
              pdata2  = l->data;
	      pclass2 = glade_property_get_class (pdata2->property);
	      widget2 = glade_property_get_widget (pdata2->property);
552

553
554
              if (widget1 == widget2 &&
                  glade_property_class_match (pclass1, pclass2))
555
556
557
558
559
560
561
562
563
564
565
566
567
                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;
568
569
570
}

static void
571
572
573
574
575
glade_command_set_property_collapse (GladeCommand * this_cmd,
                                     GladeCommand * other_cmd)
{
  GladeCommandSetProperty *cmd1, *cmd2;
  GCSetPropData *pdata1, *pdata2;
576
  GladePropertyClass *pclass1, *pclass2;
577
  GList *list, *l;
578

579
580
  g_return_if_fail (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
                    GLADE_IS_COMMAND_SET_PROPERTY (other_cmd));
581

582
583
584
585
586
587
  cmd1 = (GladeCommandSetProperty *) this_cmd;
  cmd2 = (GladeCommandSetProperty *) other_cmd;


  for (list = cmd1->sdata; list; list = list->next)
    {
588
589
590
      pdata1  = list->data;
      pclass1 = glade_property_get_class (pdata1->property);

591
592
      for (l = cmd2->sdata; l; l = l->next)
        {
593
594
          pdata2  = l->data;
	  pclass2 = glade_property_get_class (pdata2->property);
595

596
          if (glade_property_class_match (pclass1, pclass2))
597
598
599
600
601
602
603
604
            {
              /* Merge the GCSetPropData structs manually here
               */
              g_value_copy (pdata2->new_value, pdata1->new_value);
              break;
            }
        }
    }
605

606
607
  /* Set the description
   */
608
609
610
  g_free (this_cmd->priv->description);
  this_cmd->priv->description = other_cmd->priv->description;
  other_cmd->priv->description = NULL;
611
}
612

613

614
615
616
617
618
619
620
#define MAX_UNDO_MENU_ITEM_VALUE_LEN	10
static gchar *
glade_command_set_property_description (GladeCommandSetProperty * me)
{
  GCSetPropData *sdata;
  gchar *description = NULL;
  gchar *value_name;
621
622
  GladePropertyClass *pclass;
  GladeWidget *widget;
623
624
625
626
627
628
629

  g_assert (me->sdata);

  if (g_list_length (me->sdata) > 1)
    description = g_strdup_printf (_("Setting multiple properties"));
  else
    {
630
      sdata  = me->sdata->data;
631
632
      pclass = glade_property_get_class (sdata->property);
      widget = glade_property_get_widget (sdata->property);
633
      value_name = glade_widget_adaptor_string_from_value
634
	(glade_property_class_get_adaptor (pclass), pclass, sdata->new_value);
635
636
637
638
639

      if (!value_name || strlen (value_name) > MAX_UNDO_MENU_ITEM_VALUE_LEN
          || strchr (value_name, '_'))
        {
          description = g_strdup_printf (_("Setting %s of %s"),
640
                                         glade_property_class_get_name (pclass),
641
                                         glade_widget_get_name (widget));
642
643
644
645
        }
      else
        {
          description = g_strdup_printf (_("Setting %s of %s to %s"),
646
                                         glade_property_class_get_name (pclass),
647
                                         glade_widget_get_name (widget),
648
649
650
651
652
653
                                         value_name);
        }
      g_free (value_name);
    }

  return description;
654
655
}

656

657
void
658
glade_command_set_properties_list (GladeProject * project, GList * props)
659
{
660
661
662
663
664
665
  GladeCommandSetProperty *me;
  GladeCommand *cmd;
  GCSetPropData *sdata;
  GList *list;
  gboolean success;
  gboolean multiple;
666

667
668
  g_return_if_fail (GLADE_IS_PROJECT (project));
  g_return_if_fail (props);
669

670
671
672
  me = (GladeCommandSetProperty *)
      g_object_new (GLADE_COMMAND_SET_PROPERTY_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
673
  cmd->priv->project = project;
674

675
676
677
678
679
680
  /* Ref all props */
  for (list = props; list; list = list->next)
    {
      sdata = list->data;
      g_object_ref (G_OBJECT (sdata->property));
    }
681

682
  me->sdata = props;
683
  cmd->priv->description = glade_command_set_property_description (me);
684

685
686
  multiple = g_list_length (me->sdata) > 1;
  if (multiple)
687
    glade_command_push_group (cmd->priv->description);
688

689
  glade_command_check_group (GLADE_COMMAND (me));
690

691
  /* Push onto undo stack only if it executes successfully. */
692
  success = glade_command_set_property_execute (cmd);
693
694

  if (success)
695
    glade_project_push_undo (cmd->priv->project, cmd);
696
697
698
699
700
  else
    g_object_unref (G_OBJECT (me));

  if (multiple)
    glade_command_pop_group ();
701
702
703
704
}


void
705
706
707
708
709
710
glade_command_set_properties (GladeProperty * property,
                              const GValue * old_value,
                              const GValue * new_value, ...)
{
  GCSetPropData *sdata;
  GladeProperty *prop;
711
712
  GladeWidget   *widget;
  GladeProject  *project;
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
  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);

752
753
754
  widget  = glade_property_get_widget (property);
  project = glade_widget_get_project (widget);
  glade_command_set_properties_list (project, list);
755
756
}

757
void
758
759
glade_command_set_property_value (GladeProperty * property,
                                  const GValue * pvalue)
760
{
761

762
763
764
765
  /* Dont generate undo/redo items for unchanging property values.
   */
  if (glade_property_equals_value (property, pvalue))
    return;
766

767
  glade_command_set_properties (property, glade_property_inline_value (property), pvalue, NULL);
768
769
}

770
void
771
glade_command_set_property (GladeProperty * property, ...)
772
{
773
774
775
776
  GValue *value;
  va_list args;

  g_return_if_fail (GLADE_IS_PROPERTY (property));
777

778
  va_start (args, property);
779
  value = glade_property_class_make_gvalue_from_vl (glade_property_get_class (property), args);
780
781
782
  va_end (args);

  glade_command_set_property_value (property, value);
783
784
}

785
786
787
788
789
790
/**************************************************/
/*******       GLADE_COMMAND_SET_NAME       *******/
/**************************************************/

/* create a new GladeCommandSetName class.  Objects of this class will
 * encapsulate a "set name" operation */
791
792
793
typedef struct
{
  GladeCommand parent;
794

795
796
797
  GladeWidget *widget;
  gchar *old_name;
  gchar *name;
798
799
800
801
802
803
804
} 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))
805
806
#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))
807
808
809

/* Undo the last "set name command" */
static gboolean
810
glade_command_set_name_undo (GladeCommand * cmd)
811
{
812
  return glade_command_set_name_execute (cmd);
813
814
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
815
/*
816
817
818
819
 * Execute the set name command and revert it.  Ie, after the execution of this
 * function cmd will point to the undo action
 */
static gboolean
820
glade_command_set_name_execute (GladeCommand * cmd)
821
{
822
823
824
825
826
827
  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);
828

829
  glade_project_set_widget_name (cmd->priv->project, me->widget, me->name);
830

831
832
833
  tmp = me->old_name;
  me->old_name = me->name;
  me->name = tmp;
834

835
  return TRUE;
836
837
838
}

static void
839
glade_command_set_name_finalize (GObject * obj)
840
{
841
  GladeCommandSetName *me;
842

843
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (obj));
844

845
  me = GLADE_COMMAND_SET_NAME (obj);
846

847
848
  g_free (me->old_name);
  g_free (me->name);
849

850
  glade_command_finalize (obj);
851
852
853
}

static gboolean
854
855
glade_command_set_name_unifies (GladeCommand * this_cmd,
                                GladeCommand * other_cmd)
856
{
857
858
  GladeCommandSetName *cmd1;
  GladeCommandSetName *cmd2;
859

860
861
862
863
864
  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);
865

866
867
      return (cmd1->widget == cmd2->widget);
    }
868

869
  return FALSE;
870
871
872
}

static void
873
874
glade_command_set_name_collapse (GladeCommand * this_cmd,
                                 GladeCommand * other_cmd)
875
{
876
877
  GladeCommandSetName *nthis = GLADE_COMMAND_SET_NAME (this_cmd);
  GladeCommandSetName *nother = GLADE_COMMAND_SET_NAME (other_cmd);
878

879
880
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
                    GLADE_IS_COMMAND_SET_NAME (other_cmd));
881

882
883
884
  g_free (nthis->old_name);
  nthis->old_name = nother->old_name;
  nother->old_name = NULL;
885

886
887
  g_free (this_cmd->priv->description);
  this_cmd->priv->description =
888
      g_strdup_printf (_("Renaming %s to %s"), nthis->name, nthis->old_name);
889
890
891
892
}

/* this function takes the ownership of name */
void
893
glade_command_set_name (GladeWidget * widget, const gchar * name)
894
{
895
896
  GladeCommandSetName *me;
  GladeCommand *cmd;
897

898
  g_return_if_fail (GLADE_IS_WIDGET (widget));
899
  g_return_if_fail (name && name[0]);
900

901
902
  /* Dont spam the queue with false name changes.
   */
903
  if (!strcmp (glade_widget_get_name (widget), name))
904
    return;
905

906
907
  me = g_object_new (GLADE_COMMAND_SET_NAME_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
908
  cmd->priv->project = glade_widget_get_project (widget);
909

910
911
  me->widget = widget;
  me->name = g_strdup (name);
912
  me->old_name = g_strdup (glade_widget_get_name (widget));
913

914
  cmd->priv->description =
915
      g_strdup_printf (_("Renaming %s to %s"), me->old_name, me->name);
916

917
  glade_command_check_group (GLADE_COMMAND (me));
918

919
  if (glade_command_set_name_execute (GLADE_COMMAND (me)))
920
    glade_project_push_undo (cmd->priv->project, cmd);
921
922
  else
    g_object_unref (G_OBJECT (me));
923
924
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
925
926
927
928
929
930
931
/******************************************************************************
 * 
 * add/remove
 * 
 * These canonical commands add/remove a widget list to/from the project.
 * 
 *****************************************************************************/
932

933
934
935
936
937
938
typedef struct
{
  GladeCommand parent;
  GList *widgets;
  gboolean add;
  gboolean from_clipboard;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
939
} GladeCommandAddRemove;
940

Tristan Van Berkom's avatar
Tristan Van Berkom committed
941
942
943
944
945
946
947

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))
948

949
950
static void
glade_command_placeholder_destroyed (GtkWidget * object, CommandData * cdata)
951
{
952
953
954
955
956
  if (GTK_WIDGET (cdata->placeholder) == object)
    {
      cdata->placeholder = NULL;
      cdata->handler_id = 0;
    }
957
958
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
959
static void
960
961
glade_command_placeholder_connect (CommandData * cdata,
                                   GladePlaceholder * placeholder)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
962
{
963
  g_assert (cdata && cdata->placeholder == NULL);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
964

965
966
967
  /* Something like a no-op with no placeholder */
  if ((cdata->placeholder = placeholder) == NULL)
    return;
968

969
970
971
  cdata->handler_id = g_signal_connect
      (placeholder, "destroy",
       G_CALLBACK (glade_command_placeholder_destroyed), cdata);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
972
973
}

974
975

static GList *
976
get_all_parentless_reffed_widgets (GList * reffed, GladeWidget * widget)
977
{
978
979
  GList *children, *l, *list;
  GladeWidget *child;
980

981
982
  if ((list = glade_widget_get_parentless_reffed_widgets (widget)) != NULL)
    reffed = g_list_concat (reffed, list);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
983

984
  children = glade_widget_get_children (widget);
985

986
987
  for (l = children; l; l = l->next)
    {
988
989
      child  = glade_widget_get_from_gobject (l->data);
      reffed = get_all_parentless_reffed_widgets (reffed, child);
990
    }
991

992
  g_list_free (children);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
993

994
  return reffed;
995
996
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
997
998
999
1000
1001
/**
 * glade_command_add:
 * @widgets: a #Glist
 * @parent: a #GladeWidget
 * @placeholder: a #GladePlaceholder
1002
 * @pasting: whether we are pasting an existing widget or creating a new one.
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1003
1004
1005
1006
 *
 * 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).
1007
1008
1009
 * Pasted widgets will persist packing properties from thier cut/copy source
 * while newly added widgets will prefer packing defaults.
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1010
 */
1011
void
1012
1013
1014
1015
1016
glade_command_add (GList            *widgets,
                   GladeWidget      *parent,
                   GladePlaceholder *placeholder, 
		   GladeProject     *project,
		   gboolean          pasting)
1017
1018
{
  GladeCommandAddRemove *me;
1019
  GladeCommand *cmd;
1020
1021
  CommandData *cdata;
  GladeWidget *widget = NULL;
1022
  GladeWidgetAdaptor *adaptor;