glade-command.c 69.6 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

/* 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).
 */
58
59
60
61
62
63
64
65
66
67
typedef struct
{
  GladeWidget *widget;
  GladeWidget *parent;
  GList *reffed;
  GladePlaceholder *placeholder;
  gboolean props_recorded;
  GList *pack_props;
  gchar *special_type;
  gulong handler_id;
68
} CommandData;
69

70
71
72
73
static GObjectClass *parent_class = NULL;

/* Group description used for the current group
 */
74
static gchar *gc_group_description = NULL;
75
76
77

/* Use an id to avoid grouping together consecutive groups
 */
78
static gint gc_group_id = 1;
79
80
81

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

84
85

#define MAKE_TYPE(func, type, parent)			\
86
GType							\
87
88
func ## _get_type (void)				\
{							\
89
	static GType cmd_type = 0;			\
90
							\
91
	if (!cmd_type)					\
92
	{						\
93
		static const GTypeInfo info =		\
94
95
		{					\
			sizeof (type ## Class),		\
96
97
98
99
			(GBaseInitFunc) NULL,		\
			(GBaseFinalizeFunc) NULL,	\
			(GClassInitFunc) func ## _class_init,	\
			(GClassFinalizeFunc) NULL,	\
100
101
102
			NULL,				\
			sizeof (type),			\
			0,				\
103
			(GInstanceInitFunc) NULL	\
104
105
		};					\
							\
106
		cmd_type = g_type_register_static (parent, #type, &info, 0);	\
107
108
	}						\
							\
109
	return cmd_type;				\
110
111
112
}							\

static void
113
glade_command_finalize (GObject * obj)
114
{
115
116
  GladeCommand *cmd = (GladeCommand *) obj;
  g_return_if_fail (cmd != NULL);
117

118
119
  if (cmd->description)
    g_free (cmd->description);
120

121
122
  /* Call the base class dtor */
  (*G_OBJECT_CLASS (parent_class)->finalize) (obj);
123
124
125
}

static gboolean
126
glade_command_unifies_impl (GladeCommand * this_cmd, GladeCommand * other_cmd)
127
{
128
  return FALSE;
129
130
131
}

static void
132
glade_command_collapse_impl (GladeCommand * this_cmd, GladeCommand * other_cmd)
133
{
134
  g_return_if_reached ();
135
136
137
}

static void
138
glade_command_class_init (GladeCommandClass * klass)
139
{
140
  GObjectClass *object_class;
141

142
143
  object_class = G_OBJECT_CLASS (klass);
  parent_class = g_type_class_peek_parent (klass);
144

145
  object_class->finalize = glade_command_finalize;
146

147
148
149
150
  klass->undo = NULL;
  klass->execute = NULL;
  klass->unifies = glade_command_unifies_impl;
  klass->collapse = glade_command_collapse_impl;
151
152
}

153
154
/* compose the _get_type function for GladeCommand */
MAKE_TYPE (glade_command, GladeCommand, G_TYPE_OBJECT)
155
156
157
158
159
160
161
162
#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								\
163
func ## _unifies (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
164
static void								\
165
func ## _collapse (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
166
167
168
169
170
static void								\
func ## _class_init (gpointer parent_tmp, gpointer notused)		\
{									\
	GladeCommandClass *parent = parent_tmp;				\
	GObjectClass* object_class;					\
171
	object_class = G_OBJECT_CLASS (parent);				\
172
173
	parent->undo =  func ## _undo;					\
	parent->execute =  func ## _execute;				\
174
175
	parent->unifies =  func ## _unifies;				\
	parent->collapse =  func ## _collapse;				\
176
177
178
179
180
	object_class->finalize = func ## _finalize;			\
}									\
typedef struct {							\
	GladeCommandClass cmd;						\
} type ## Class;							\
181
static MAKE_TYPE(func, type, GLADE_TYPE_COMMAND)
182
183
184
185
186
187
188
189
/**
 * glade_command_execute:
 * @command: A #GladeCommand
 *
 * Executes @command
 *
 * Returns: whether the command was successfully executed
 */
190
191
    gboolean
glade_command_execute (GladeCommand * command)
192
{
193
194
  g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
  return GLADE_COMMAND_GET_CLASS (command)->execute (command);
195
196
197
}


198
199
200
201
202
203
/**
 * glade_command_undo:
 * @command: A #GladeCommand
 *
 * Undo the effects of @command
 *
204
 * Returns: whether the command was successfully reversed
205
206
 */
gboolean
207
glade_command_undo (GladeCommand * command)
208
{
209
210
  g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
  return GLADE_COMMAND_GET_CLASS (command)->undo (command);
211
212
}

213
/**
214
215
216
217
218
219
 * glade_command_unifies:
 * @command: A #GladeCommand
 * @other: another #GladeCommand
 *
 * Checks whether @command and @other can be unified
 * to make one single command.
220
 *
221
 * Returns: whether they can be unified.
222
 */
223
gboolean
224
glade_command_unifies (GladeCommand * command, GladeCommand * other)
225
{
226
  g_return_val_if_fail (command, FALSE);
227

228
229
230
231
232
  /* Cannot unify with a part of a command group.
   * Unify atomic commands only
   */
  if (command->group_id != 0 || (other && other->group_id != 0))
    return FALSE;
233

234
  return GLADE_COMMAND_GET_CLASS (command)->unifies (command, other);
235
236
}

237
/**
238
239
240
 * glade_command_collapse:
 * @command: A #GladeCommand
 * @other: another #GladeCommand
241
 *
242
243
 * Merges @other into @command, so that @command now
 * covers both commands and @other can be dispensed with.
244
 */
245
void
246
glade_command_collapse (GladeCommand * command, GladeCommand * other)
247
{
248
249
  g_return_if_fail (command);
  GLADE_COMMAND_GET_CLASS (command)->collapse (command, other);
250
251
252
253
}

/**
 * glade_command_push_group:
254
 * @fmt:         The collective desctiption of the command group.
255
256
 *               only the description of the first group on the 
 *               stack is used when embedding groups.
257
 * @...: args to the format string.
258
259
260
261
 *
 * Marks the begining of a group.
 */
void
262
glade_command_push_group (const gchar * fmt, ...)
263
{
264
  va_list args;
265

266
  g_return_if_fail (fmt);
267

268
269
270
271
272
273
274
275
  /* 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);
    }
276
277
278
279
280
281
282
283
284
285
}

/**
 * glade_command_pop_group:
 *
 * Mark the end of a command group.
 */
void
glade_command_pop_group (void)
{
286
287
288
289
290
  if (gc_group_depth-- == 1)
    {
      gc_group_description = (g_free (gc_group_description), NULL);
      gc_group_id++;
    }
291

292
293
  if (gc_group_depth < 0)
    g_critical ("Unbalanced group stack detected in %s\n", G_STRFUNC);
294
}
295

296
297
298
gint
glade_command_get_group_depth (void)
{
299
  return gc_group_depth;
300
301
}

302
static void
303
glade_command_check_group (GladeCommand * cmd)
304
{
305
306
307
308
309
310
311
  g_return_if_fail (GLADE_IS_COMMAND (cmd));
  if (gc_group_description)
    {
      cmd->description =
          (g_free (cmd->description), g_strdup (gc_group_description));
      cmd->group_id = gc_group_id;
    }
312
313
}

314
315
/**************************************************/
/*******     GLADE_COMMAND_SET_PROPERTY     *******/
316
317
/**************************************************/

318
319
/* create a new GladeCommandSetProperty class.  Objects of this class will
 * encapsulate a "set property" operation */
320

321
322
323
324
325
326
typedef struct
{
  GladeCommand parent;
  gboolean set_once;
  gboolean undo;
  GList *sdata;
327
} GladeCommandSetProperty;
328

329
/* standard macros */
330
331
332
333
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))
334
335
#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))
336

337
/* Undo the last "set property command" */
338
static gboolean
339
glade_command_set_property_undo (GladeCommand * cmd)
340
{
341
  return glade_command_set_property_execute (cmd);
342
343
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
344
/*
345
346
 * Execute the set property command and revert it. IE, after the execution of 
 * this function cmd will point to the undo action
347
348
 */
static gboolean
349
glade_command_set_property_execute (GladeCommand * cmd)
350
{
351
352
353
354
355
356
  GladeCommandSetProperty *me = (GladeCommandSetProperty *) cmd;
  GList *l;
  gboolean success;
  gboolean retval = FALSE;

  g_return_val_if_fail (me != NULL, FALSE);
357

358
359
  if (me->set_once != FALSE)
    glade_property_push_superuser ();
360

361
362
  for (l = me->sdata; l; l = l->next)
    {
363
364
365
366
      GValue              new_value = { 0, };
      GCSetPropData      *sdata = l->data;
      GladePropertyClass *pclass = glade_property_get_class (sdata->property);
      GladeWidget        *widget = glade_property_get_widget (sdata->property);
367

368
      g_value_init (&new_value, G_VALUE_TYPE (sdata->new_value));
369

370
371
372
373
      if (me->undo)
        g_value_copy (sdata->old_value, &new_value);
      else
        g_value_copy (sdata->new_value, &new_value);
374

375
#if 0
376
377
378
      {
        gchar *str =
            glade_widget_adaptor_string_from_value
379
            (GLADE_WIDGET_ADAPTOR (pclass->handle), pclass, &new_value);
380
381

        g_print ("Setting %s property of %s to %s (sumode: %d)\n",
382
383
                 pclass->id,
                 glade_widget_get_name (widget),
384
385
386
387
                 str, glade_property_superuser ());

        g_free (str);
      }
388
389
#endif

390
391
392
      /* Packing properties need to be refreshed here since
       * they are reset when they get added to containers.
       */
393
      if (glade_property_class_get_is_packing (pclass))
394
395
        {
          GladeProperty *tmp_prop;
396

397
          tmp_prop = glade_widget_get_pack_property (widget, glade_property_class_id (pclass));
398

399
400
401
402
403
404
          if (sdata->property != tmp_prop)
            {
              g_object_unref (sdata->property);
              sdata->property = g_object_ref (tmp_prop);
            }
        }
405

406
      success = glade_property_set_value (sdata->property, &new_value);
407
      retval  = retval || success;
408

409
410
411
412
      if (!me->set_once && success)
        {
          /* If some verify functions didnt pass on 
           * the first go.. we need to record the actual
413
           * properties here. XXX should be able to use glade_property_get_value() here
414
           */
415
          g_value_copy (glade_property_inline_value (sdata->property), sdata->new_value);
416
        }
417

418
419
      g_value_unset (&new_value);
    }
420

421
422
  if (me->set_once != FALSE)
    glade_property_pop_superuser ();
423

424
425
426
427
  me->set_once = TRUE;
  me->undo = !me->undo;

  return retval;
428
429
430
}

static void
431
glade_command_set_property_finalize (GObject * obj)
432
{
433
434
435
436
  GladeCommandSetProperty *me;
  GList *l;

  me = GLADE_COMMAND_SET_PROPERTY (obj);
437

438
439
440
  for (l = me->sdata; l; l = l->next)
    {
      GCSetPropData *sdata = l->data;
441

442
443
      if (sdata->property)
        g_object_unref (G_OBJECT (sdata->property));
444

445
446
447
448
449
450
451
452
453
      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);
454

455
456
    }
  glade_command_finalize (obj);
457
458
459
}

static gboolean
460
461
462
463
glade_command_set_property_unifies (GladeCommand * this_cmd,
                                    GladeCommand * other_cmd)
{
  GladeCommandSetProperty *cmd1, *cmd2;
464
  GladePropertyClass *pclass1, *pclass2;
465
  GCSetPropData *pdata1, *pdata2;
466
  GladeWidget *widget1, *widget2;
467
468
469
470
471
472
473
474
475
476
  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)
            {
477
478
              pdata1  = list->data;
	      pclass1 = glade_property_get_class (pdata1->property);
479

480
              if (glade_property_class_compare (pclass1,
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
                                                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)
        {
502
503
504
505
          pdata1  = list->data;
	  pclass1 = glade_property_get_class (pdata1->property);
	  widget1 = glade_property_get_widget (pdata1->property);

506
507
          for (l = cmd2->sdata; l; l = l->next)
            {
508
509
510
              pdata2  = l->data;
	      pclass2 = glade_property_get_class (pdata2->property);
	      widget2 = glade_property_get_widget (pdata2->property);
511

512
513
              if (widget1 == widget2 &&
                  glade_property_class_match (pclass1, pclass2))
514
515
516
517
518
519
520
521
522
523
524
525
526
                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;
527
528
529
}

static void
530
531
532
533
534
glade_command_set_property_collapse (GladeCommand * this_cmd,
                                     GladeCommand * other_cmd)
{
  GladeCommandSetProperty *cmd1, *cmd2;
  GCSetPropData *pdata1, *pdata2;
535
  GladePropertyClass *pclass1, *pclass2;
536
  GList *list, *l;
537

538
539
  g_return_if_fail (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
                    GLADE_IS_COMMAND_SET_PROPERTY (other_cmd));
540

541
542
543
544
545
546
  cmd1 = (GladeCommandSetProperty *) this_cmd;
  cmd2 = (GladeCommandSetProperty *) other_cmd;


  for (list = cmd1->sdata; list; list = list->next)
    {
547
548
549
      pdata1  = list->data;
      pclass1 = glade_property_get_class (pdata1->property);

550
551
      for (l = cmd2->sdata; l; l = l->next)
        {
552
553
          pdata2  = l->data;
	  pclass2 = glade_property_get_class (pdata2->property);
554

555
          if (glade_property_class_match (pclass1, pclass2))
556
557
558
559
560
561
562
563
            {
              /* Merge the GCSetPropData structs manually here
               */
              g_value_copy (pdata2->new_value, pdata1->new_value);
              break;
            }
        }
    }
564

565
566
567
568
569
570
  /* Set the description
   */
  g_free (this_cmd->description);
  this_cmd->description = other_cmd->description;
  other_cmd->description = NULL;
}
571

572

573
574
575
576
577
578
579
#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;
580
581
  GladePropertyClass *pclass;
  GladeWidget *widget;
582
583
584
585
586
587
588

  g_assert (me->sdata);

  if (g_list_length (me->sdata) > 1)
    description = g_strdup_printf (_("Setting multiple properties"));
  else
    {
589
      sdata  = me->sdata->data;
590
591
      pclass = glade_property_get_class (sdata->property);
      widget = glade_property_get_widget (sdata->property);
592
      value_name = glade_widget_adaptor_string_from_value
593
	(glade_property_class_get_adaptor (pclass), pclass, sdata->new_value);
594
595
596
597
598

      if (!value_name || strlen (value_name) > MAX_UNDO_MENU_ITEM_VALUE_LEN
          || strchr (value_name, '_'))
        {
          description = g_strdup_printf (_("Setting %s of %s"),
599
                                         glade_property_class_get_name (pclass),
600
                                         glade_widget_get_name (widget));
601
602
603
604
        }
      else
        {
          description = g_strdup_printf (_("Setting %s of %s to %s"),
605
                                         glade_property_class_get_name (pclass),
606
                                         glade_widget_get_name (widget),
607
608
609
610
611
612
                                         value_name);
        }
      g_free (value_name);
    }

  return description;
613
614
}

615

616
void
617
glade_command_set_properties_list (GladeProject * project, GList * props)
618
{
619
620
621
622
623
624
  GladeCommandSetProperty *me;
  GladeCommand *cmd;
  GCSetPropData *sdata;
  GList *list;
  gboolean success;
  gboolean multiple;
625

626
627
  g_return_if_fail (GLADE_IS_PROJECT (project));
  g_return_if_fail (props);
628

629
630
631
  me = (GladeCommandSetProperty *)
      g_object_new (GLADE_COMMAND_SET_PROPERTY_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
632
  cmd->project = project;
633

634
635
636
637
638
639
  /* Ref all props */
  for (list = props; list; list = list->next)
    {
      sdata = list->data;
      g_object_ref (G_OBJECT (sdata->property));
    }
640

641
642
  me->sdata = props;
  cmd->description = glade_command_set_property_description (me);
643

644
645
646
  multiple = g_list_length (me->sdata) > 1;
  if (multiple)
    glade_command_push_group (cmd->description);
647

648
  glade_command_check_group (GLADE_COMMAND (me));
649

650
  /* Push onto undo stack only if it executes successfully. */
651
  success = glade_command_set_property_execute (cmd);
652
653

  if (success)
654
    glade_project_push_undo (cmd->project, cmd);
655
656
657
658
659
  else
    g_object_unref (G_OBJECT (me));

  if (multiple)
    glade_command_pop_group ();
660
661
662
663
}


void
664
665
666
667
668
669
glade_command_set_properties (GladeProperty * property,
                              const GValue * old_value,
                              const GValue * new_value, ...)
{
  GCSetPropData *sdata;
  GladeProperty *prop;
670
671
  GladeWidget   *widget;
  GladeProject  *project;
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
  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);

711
712
713
  widget  = glade_property_get_widget (property);
  project = glade_widget_get_project (widget);
  glade_command_set_properties_list (project, list);
714
715
}

716
void
717
718
glade_command_set_property_value (GladeProperty * property,
                                  const GValue * pvalue)
719
{
720

721
722
723
724
  /* Dont generate undo/redo items for unchanging property values.
   */
  if (glade_property_equals_value (property, pvalue))
    return;
725

726
  glade_command_set_properties (property, glade_property_inline_value (property), pvalue, NULL);
727
728
}

729
void
730
glade_command_set_property (GladeProperty * property, ...)
731
{
732
733
734
735
  GValue *value;
  va_list args;

  g_return_if_fail (GLADE_IS_PROPERTY (property));
736

737
  va_start (args, property);
738
  value = glade_property_class_make_gvalue_from_vl (glade_property_get_class (property), args);
739
740
741
  va_end (args);

  glade_command_set_property_value (property, value);
742
743
}

744
745
746
747
748
749
/**************************************************/
/*******       GLADE_COMMAND_SET_NAME       *******/
/**************************************************/

/* create a new GladeCommandSetName class.  Objects of this class will
 * encapsulate a "set name" operation */
750
751
752
typedef struct
{
  GladeCommand parent;
753

754
755
756
  GladeWidget *widget;
  gchar *old_name;
  gchar *name;
757
758
759
760
761
762
763
} 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))
764
765
#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))
766
767
768

/* Undo the last "set name command" */
static gboolean
769
glade_command_set_name_undo (GladeCommand * cmd)
770
{
771
  return glade_command_set_name_execute (cmd);
772
773
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
774
/*
775
776
777
778
 * Execute the set name command and revert it.  Ie, after the execution of this
 * function cmd will point to the undo action
 */
static gboolean
779
glade_command_set_name_execute (GladeCommand * cmd)
780
{
781
782
783
784
785
786
  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);
787

788
  glade_project_set_widget_name (cmd->project, me->widget, me->name);
789

790
791
792
  tmp = me->old_name;
  me->old_name = me->name;
  me->name = tmp;
793

794
  return TRUE;
795
796
797
}

static void
798
glade_command_set_name_finalize (GObject * obj)
799
{
800
  GladeCommandSetName *me;
801

802
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (obj));
803

804
  me = GLADE_COMMAND_SET_NAME (obj);
805

806
807
  g_free (me->old_name);
  g_free (me->name);
808

809
  glade_command_finalize (obj);
810
811
812
}

static gboolean
813
814
glade_command_set_name_unifies (GladeCommand * this_cmd,
                                GladeCommand * other_cmd)
815
{
816
817
  GladeCommandSetName *cmd1;
  GladeCommandSetName *cmd2;
818

819
820
821
822
823
  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);
824

825
826
      return (cmd1->widget == cmd2->widget);
    }
827

828
  return FALSE;
829
830
831
}

static void
832
833
glade_command_set_name_collapse (GladeCommand * this_cmd,
                                 GladeCommand * other_cmd)
834
{
835
836
  GladeCommandSetName *nthis = GLADE_COMMAND_SET_NAME (this_cmd);
  GladeCommandSetName *nother = GLADE_COMMAND_SET_NAME (other_cmd);
837

838
839
  g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
                    GLADE_IS_COMMAND_SET_NAME (other_cmd));
840

841
842
843
  g_free (nthis->old_name);
  nthis->old_name = nother->old_name;
  nother->old_name = NULL;
844

845
846
847
  g_free (this_cmd->description);
  this_cmd->description =
      g_strdup_printf (_("Renaming %s to %s"), nthis->name, nthis->old_name);
848
849
850
851
}

/* this function takes the ownership of name */
void
852
glade_command_set_name (GladeWidget * widget, const gchar * name)
853
{
854
855
  GladeCommandSetName *me;
  GladeCommand *cmd;
856

857
  g_return_if_fail (GLADE_IS_WIDGET (widget));
858
  g_return_if_fail (name && name[0]);
859

860
861
  /* Dont spam the queue with false name changes.
   */
862
  if (!strcmp (glade_widget_get_name (widget), name))
863
    return;
864

865
866
  me = g_object_new (GLADE_COMMAND_SET_NAME_TYPE, NULL);
  cmd = GLADE_COMMAND (me);
867
  cmd->project = glade_widget_get_project (widget);
868

869
870
  me->widget = widget;
  me->name = g_strdup (name);
871
  me->old_name = g_strdup (glade_widget_get_name (widget));
872

873
874
  cmd->description =
      g_strdup_printf (_("Renaming %s to %s"), me->old_name, me->name);
875

876
  glade_command_check_group (GLADE_COMMAND (me));
877

878
  if (glade_command_set_name_execute (GLADE_COMMAND (me)))
879
    glade_project_push_undo (cmd->project, cmd);
880
881
  else
    g_object_unref (G_OBJECT (me));
882
883
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
884
885
886
887
888
889
890
/******************************************************************************
 * 
 * add/remove
 * 
 * These canonical commands add/remove a widget list to/from the project.
 * 
 *****************************************************************************/
891

892
893
894
895
896
897
typedef struct
{
  GladeCommand parent;
  GList *widgets;
  gboolean add;
  gboolean from_clipboard;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
898
} GladeCommandAddRemove;
899

Tristan Van Berkom's avatar
Tristan Van Berkom committed
900
901
902
903
904
905
906

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

908
909
static void
glade_command_placeholder_destroyed (GtkWidget * object, CommandData * cdata)
910
{
911
912
913
914
915
  if (GTK_WIDGET (cdata->placeholder) == object)
    {
      cdata->placeholder = NULL;
      cdata->handler_id = 0;
    }
916
917
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
918
static void
919
920
glade_command_placeholder_connect (CommandData * cdata,
                                   GladePlaceholder * placeholder)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
921
{
922
  g_assert (cdata && cdata->placeholder == NULL);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
923

924
925
926
  /* Something like a no-op with no placeholder */
  if ((cdata->placeholder = placeholder) == NULL)
    return;
927

928
929
930
  cdata->handler_id = g_signal_connect
      (placeholder, "destroy",
       G_CALLBACK (glade_command_placeholder_destroyed), cdata);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
931
932
}

933
934

static GList *
935
get_all_parentless_reffed_widgets (GList * reffed, GladeWidget * widget)
936
{
937
938
  GList *children, *l, *list;
  GladeWidget *child;
939

940
941
  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
942

943
  children = glade_widget_get_children (widget);
944

945
946
  for (l = children; l; l = l->next)
    {
947
948
      child  = glade_widget_get_from_gobject (l->data);
      reffed = get_all_parentless_reffed_widgets (reffed, child);
949
    }
950

951
  g_list_free (children);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
952

953
  return reffed;
954
955
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
956
957
958
959
960
/**
 * glade_command_add:
 * @widgets: a #Glist
 * @parent: a #GladeWidget
 * @placeholder: a #GladePlaceholder
961
 * @pasting: whether we are pasting an existing widget or creating a new one.
Tristan Van Berkom's avatar
Tristan Van Berkom committed
962
963
964
965
 *
 * 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).
966
967
968
 * 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
969
 */
970
static void
971
972
973
974
975
glade_command_add (GList            *widgets,
                   GladeWidget      *parent,
                   GladePlaceholder *placeholder, 
		   GladeProject     *project,
		   gboolean          pasting)
976
977
{
  GladeCommandAddRemove *me;
978
  GladeCommand *cmd;
979
980
  CommandData *cdata;
  GladeWidget *widget = NULL;
981
  GladeWidgetAdaptor *adaptor;
982
983
984
985
986
987
988
  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);
989
  cmd = GLADE_COMMAND (me);
990
991
992
993
994
995
996
  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.
   */
997
998
999
1000
  widget  = GLADE_WIDGET (widgets->data);
  adaptor = glade_widget_get_adaptor (widget);

  if (placeholder && GWA_IS_TOPLEVEL (adaptor) == FALSE)
1001
    cmd->project = glade_placeholder_get_project (placeholder);
1002
  else
1003
    cmd->project = project;