glade-command.c 55.4 KB
Newer Older
1
2
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
3
 * Copyright (C) 2002 Joaquín Cuenca Abela
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * 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
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19
 *
 * Authors:
20
 *   Joaquín Cuenca Abela <e98cuenc@yahoo.com>
21
 *   Archit Baweja <bighead@users.sourceforge.net>
22
 */
23
24
25
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
Paolo Borelli's avatar
Paolo Borelli committed
26

27
28
29
30
31
32
33
34
/**
 * 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.
 */

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

/* 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).
 */
59
60
61
typedef struct {
	GladeWidget      *widget;
	GladeWidget      *parent;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
62
	GladeProject     *project;
63
	GladePlaceholder *placeholder;
64
	gboolean          props_recorded;
65
	GList            *pack_props;
66
	gchar            *special_type;
67
	gulong            handler_id;
68
} CommandData;
69

70
71
72
73
74
75
76
77
78
79
80
81
82
83
static GObjectClass *parent_class = NULL;

/* Group description used for the current group
 */
static gchar        *gc_group_description = NULL;

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

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

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
113
114
115
116
117
}							\

static void
glade_command_finalize (GObject *obj)
{
        GladeCommand *cmd = (GladeCommand *) obj;
        g_return_if_fail (cmd != NULL);

118
119
	if (cmd->description)
		g_free (cmd->description);
120
121
122
123
124
125

        /* Call the base class dtor */
	(* G_OBJECT_CLASS (parent_class)->finalize) (obj);
}

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

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

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

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

145
146
	object_class->finalize = glade_command_finalize;

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
155
/* compose the _get_type function for GladeCommand */
MAKE_TYPE (glade_command, GladeCommand, G_TYPE_OBJECT)

156
157
158
159
160
161
162
163
164

#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								\
165
func ## _unifies (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
166
static void								\
167
func ## _collapse (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
168
169
170
171
172
static void								\
func ## _class_init (gpointer parent_tmp, gpointer notused)		\
{									\
	GladeCommandClass *parent = parent_tmp;				\
	GObjectClass* object_class;					\
173
	object_class = G_OBJECT_CLASS (parent);				\
174
175
	parent->undo =  func ## _undo;					\
	parent->execute =  func ## _execute;				\
176
177
	parent->unifies =  func ## _unifies;				\
	parent->collapse =  func ## _collapse;				\
178
179
180
181
182
	object_class->finalize = func ## _finalize;			\
}									\
typedef struct {							\
	GladeCommandClass cmd;						\
} type ## Class;							\
183
static MAKE_TYPE(func, type, GLADE_TYPE_COMMAND)
184

185

186
187
188
189
190
191
192
193
194
195
/**
 * glade_command_execute:
 * @command: A #GladeCommand
 *
 * Executes @command
 *
 * Returns: whether the command was successfully executed
 */
gboolean
glade_command_execute (GladeCommand *command)
196
{
197
	g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
198
	return GLADE_COMMAND_GET_CLASS (command)->execute (command);
199
200
201
}


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

217
/**
218
219
220
221
222
223
 * glade_command_unifies:
 * @command: A #GladeCommand
 * @other: another #GladeCommand
 *
 * Checks whether @command and @other can be unified
 * to make one single command.
224
 *
225
 * Returns: whether they can be unified.
226
 */
227
228
229
gboolean
glade_command_unifies (GladeCommand *command,
		       GladeCommand *other)
230
{
231
232
	g_return_val_if_fail (command, FALSE);
	return GLADE_COMMAND_GET_CLASS (command)->unifies (command, other);
233
234
}

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

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

	g_return_if_fail (fmt);
266
267
268
269

	/* Only use the description for the first group.
	 */
	if (gc_group_depth++ == 0)
270
271
272
273
274
	{
		va_start (args, fmt);
		gc_group_description = g_strdup_vprintf (fmt, args);
		va_end (args);
	}
275
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)
{
	if (gc_group_depth-- == 1)
286
	{
287
288
		gc_group_description = (g_free (gc_group_description), NULL);
		gc_group_id++;
289
290
	}

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

296
297
298
299
300
301
302
303
304
305
static void
glade_command_check_group (GladeCommand *cmd)
{
	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;
	}
306
307
}

308
309
/**************************************************/
/*******     GLADE_COMMAND_SET_PROPERTY     *******/
310
311
/**************************************************/

312
313
/* create a new GladeCommandSetProperty class.  Objects of this class will
 * encapsulate a "set property" operation */
314
315

typedef struct {
316
317
318
319
	GladeCommand  parent;
	gboolean      set_once;
	gboolean      undo;
	GList        *sdata;
320
} GladeCommandSetProperty;
321

322
/* standard macros */
323
324
325
326
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))
327
328
#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))
329

330
/* Undo the last "set property command" */
331
static gboolean
332
glade_command_set_property_undo (GladeCommand *cmd)
333
{
334
	return glade_command_set_property_execute (cmd);
335
336
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
337
/*
338
339
 * Execute the set property command and revert it. IE, after the execution of 
 * this function cmd will point to the undo action
340
341
 */
static gboolean
342
glade_command_set_property_execute (GladeCommand *cmd)
343
{
344
	GladeCommandSetProperty* me = (GladeCommandSetProperty*) cmd;
345
	GList *l;
346

347
	g_return_val_if_fail (me != NULL, FALSE);
348

349
350
351
	if (me->set_once != FALSE)
		glade_property_push_superuser ();

352
353
354
	for (l = me->sdata; l; l = l->next)
	{
		GValue new_value = { 0, };
355
		GCSetPropData *sdata = l->data;
356

357
358
		g_value_init (&new_value, G_VALUE_TYPE (sdata->new_value));
		
359
360
361
362
363
		if (me->undo)
			g_value_copy (sdata->old_value, &new_value);
		else
			g_value_copy (sdata->new_value, &new_value);

364
#if 0
365
366
		{
			gchar *str =
367
368
369
				glade_widget_adaptor_string_from_value
				(GLADE_WIDGET_ADAPTOR (sdata->property->klass->handle),
				 sdata->property->klass, &new_value);
370
371

			g_print ("Setting %s property of %s to %s (sumode: %d)\n",
372
				 sdata->property->klass->id,
373
374
375
376
377
378
379
380
381
382
				 sdata->property->widget->name,
				 str, glade_property_superuser ());

			g_free (str);
		}
#endif

		/* Packing properties need to be refreshed here since
		 * they are reset when they get added to containers.
		 */
383
		if (sdata->property->klass->packing)
384
385
386
387
		{
			GladeProperty *tmp_prop;

			tmp_prop = glade_widget_get_pack_property
388
				(sdata->property->widget, sdata->property->klass->id);
389
390
391
392
393
394
395
396
397

			if (sdata->property != tmp_prop)
			{
				g_object_unref (sdata->property);
				sdata->property = g_object_ref (tmp_prop);
				
			}
		}

398
		glade_property_set_value (sdata->property, &new_value);
399
400
401
402
403
404
405
406
407
408
409
410

		if (!me->set_once)
		{
			/* If some verify functions didnt pass on 
			 * the first go.. we need to record the actual
			 * properties here.
			 */
			g_value_copy (sdata->property->value, 
				      sdata->new_value);
		}


411
		g_value_unset (&new_value);
412

413

414
	}
415
416
417
418
419
420
421

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

	me->set_once = TRUE;
	me->undo     = !me->undo;

422
	return TRUE;
423
424
425
}

static void
426
glade_command_set_property_finalize (GObject *obj)
427
{
428
	GladeCommandSetProperty *me;
429
	GList *l;
430
431
432

	me = GLADE_COMMAND_SET_PROPERTY (obj);

433
434
	for (l = me->sdata; l; l = l->next)
	{
435
		GCSetPropData *sdata = l->data;
436
437
438
439
440
441
442
443
444
445
446
447
448
		
		if (sdata->property)
			g_object_unref (G_OBJECT (sdata->property));

		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);
449

450
	}
451
	glade_command_finalize (obj);
452
453
454
}

static gboolean
455
glade_command_set_property_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
456
{
457
458
459
       GladeCommandSetProperty *cmd1,   *cmd2;
       GCSetPropData           *pdata1, *pdata2;
       GList                   *list, *l;
460

461
462
       if (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) && 
	   GLADE_IS_COMMAND_SET_PROPERTY (other_cmd))
463
       {
464
465
               cmd1 = (GladeCommandSetProperty*) this_cmd;
               cmd2 = (GladeCommandSetProperty*) other_cmd;
466
467
468
469
470
471
472
473
474
475
476
477

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

	       for (list = cmd1->sdata; list; list = list->next)
	       {
		       pdata1 = list->data;
		       for (l = cmd2->sdata; l; l = l->next)
		       {
			       pdata2 = l->data;

478
			       if (pdata1->property->widget == pdata2->property->widget &&
479
480
				   glade_property_class_match (pdata1->property->klass,
							       pdata2->property->klass))
481
482
483
484
485
486
487
488
489
490
491
492
				       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;
493
494
       }
       return FALSE;
495
496
497
}

static void
498
glade_command_set_property_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd)
499
{
500
501
502
503
	GladeCommandSetProperty *cmd1,   *cmd2;
	GCSetPropData           *pdata1, *pdata2;
	GList                   *list, *l;
	
504
505
	g_return_if_fail (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
			  GLADE_IS_COMMAND_SET_PROPERTY (other_cmd));
506
	
507
508
	cmd1 = (GladeCommandSetProperty*) this_cmd;
	cmd2 = (GladeCommandSetProperty*) other_cmd;
509
510
511
512
513
514
515
516
517


	for (list = cmd1->sdata; list; list = list->next)
	{
		pdata1 = list->data;
		for (l = cmd2->sdata; l; l = l->next)
		{
			pdata2 = l->data;
			
518
519
			if (glade_property_class_match (pdata1->property->klass,
							pdata2->property->klass))
520
521
522
523
524
525
526
527
			{
				/* Merge the GCSetPropData structs manually here
				 */
				g_value_copy (pdata2->new_value, pdata1->new_value);
				break;
			}
		}
	}
Paolo Borelli's avatar
Paolo Borelli committed
528

529
530
	/* Set the description
	 */
531
532
533
	g_free (this_cmd->description);
	this_cmd->description = other_cmd->description;
	other_cmd->description = NULL;
534

Tristan Van Berkom's avatar
Updated    
Tristan Van Berkom committed
535
	glade_app_update_ui ();
536
537
}

538
539

#define MAX_UNDO_MENU_ITEM_VALUE_LEN	10
540
541
542
static gchar *
glade_command_set_property_description (GladeCommandSetProperty *me)
{
543
	GCSetPropData   *sdata;
544
545
546
547
548
549
550
551
552
553
	gchar *description = NULL;
	gchar *value_name;

	g_assert (me->sdata);

	if (g_list_length (me->sdata) > 1)
		description = g_strdup_printf (_("Setting multiple properties"));
	else 
	{
		sdata = me->sdata->data;
554
555
556
557
		value_name = glade_widget_adaptor_string_from_value
			(GLADE_WIDGET_ADAPTOR (sdata->property->klass->handle),
			 sdata->property->klass, sdata->new_value);

558
559
560
		if (!value_name || strlen (value_name) > MAX_UNDO_MENU_ITEM_VALUE_LEN
		    || strchr (value_name, '_')) {
			description = g_strdup_printf (_("Setting %s of %s"),
561
						       sdata->property->klass->name,
562
563
564
						       sdata->property->widget->name);
		} else {
			description = g_strdup_printf (_("Setting %s of %s to %s"),
565
						       sdata->property->klass->name,
566
567
568
569
570
571
572
573
						       sdata->property->widget->name, value_name);
		}
		g_free (value_name);
	}

	return description;
}

574

575
void
576
glade_command_set_properties_list (GladeProject *project, GList *props)
577
{
578
	GladeCommandSetProperty *me;
579
	GladeCommand  *cmd;
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
	GCSetPropData *sdata;
	GList         *list;

	g_return_if_fail (GLADE_IS_PROJECT (project));
	g_return_if_fail (props);

	me = (GladeCommandSetProperty*) g_object_new (GLADE_COMMAND_SET_PROPERTY_TYPE, NULL);
	cmd = GLADE_COMMAND (me);

	/* Ref all props */
	for (list = props; list; list = list->next)
	{
		sdata = list->data;
		g_object_ref (G_OBJECT (sdata->property));
	}

	me->sdata        = props;
	cmd->description = glade_command_set_property_description (me);
598
599

	glade_command_check_group (GLADE_COMMAND (me));
600
601
602

	/* Push onto undo stack only if it executes successfully. */
	if (glade_command_set_property_execute (GLADE_COMMAND (me)))
603
		glade_project_push_undo (GLADE_PROJECT (project),
604
605
606
607
608
609
610
611
612
613
614
					 GLADE_COMMAND (me));
	else
		/* No leaks on my shift! */
		g_object_unref (G_OBJECT (me));
}


void
glade_command_set_properties (GladeProperty *property, const GValue *old_value, const GValue *new_value, ...)
{
	GCSetPropData *sdata;
615
616
	GladeProperty *prop;
	GValue        *ovalue, *nvalue;
617
	GList         *list = NULL;
618
	va_list        vl;
Paolo Borelli's avatar
Paolo Borelli committed
619
620

	g_return_if_fail (GLADE_IS_PROPERTY (property));
621

622
	/* Add first set */
623
624
	sdata = g_new (GCSetPropData, 1);
	sdata->property = property;
625
626
627
628
629
630
	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);
631
	list = g_list_prepend (list, sdata);
632
633
634
635
636
637
638
639
640
641
642

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

643
		sdata = g_new (GCSetPropData, 1);
644
645
646
647
648
649
650
		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);
651
		list = g_list_prepend (list, sdata);
652
653
654
	}	
	va_end (vl);

655
	glade_command_set_properties_list (property->widget->project, list);
656
657
}

658
void
659
glade_command_set_property_value (GladeProperty *property, const GValue* pvalue)
660
{
661
662
663
664
665
666

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

667
668
669
	glade_command_set_properties (property, property->value, pvalue, NULL);
}

670
671
672
673
674
675
676
677
678
void
glade_command_set_property (GladeProperty *property, ...)
{
	GValue *value;
	va_list args;
	
	g_return_if_fail (GLADE_IS_PROPERTY (property));

	va_start (args, property);
679
	value = glade_property_class_make_gvalue_from_vl (property->klass, args);
680
681
682
683
684
	va_end (args);
	
	glade_command_set_property_value (property, value);
}

685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
/**************************************************/
/*******       GLADE_COMMAND_SET_NAME       *******/
/**************************************************/

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

	GladeWidget *widget;
	gchar *old_name;
	gchar *name;
} 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))
704
705
#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))
706
707
708
709
710
711
712
713

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

Tristan Van Berkom's avatar
Tristan Van Berkom committed
714
/*
715
716
717
718
719
720
 * Execute the set name command and revert it.  Ie, after the execution of this
 * function cmd will point to the undo action
 */
static gboolean
glade_command_set_name_execute (GladeCommand *cmd)
{
721
	GladeCommandSetName* me = GLADE_COMMAND_SET_NAME (cmd);
722
723
724
725
726
727
728
	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);

	glade_widget_set_name (me->widget, me->name);
729
	
730
731
732
733
	tmp = me->old_name;
	me->old_name = me->name;
	me->name = tmp;

734
	return TRUE;
735
736
737
738
739
}

static void
glade_command_set_name_finalize (GObject *obj)
{
740
741
742
743
744
745
	GladeCommandSetName* me;

	g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (obj));

	me = GLADE_COMMAND_SET_NAME (obj);

746
747
	g_free (me->old_name);
	g_free (me->name);
748

749
750
751
752
	glade_command_finalize (obj);
}

static gboolean
753
glade_command_set_name_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
754
755
756
757
{
	GladeCommandSetName *cmd1;
	GladeCommandSetName *cmd2;

758
	if (GLADE_IS_COMMAND_SET_NAME (this_cmd) && GLADE_IS_COMMAND_SET_NAME (other_cmd))
759
	{
760
761
		cmd1 = GLADE_COMMAND_SET_NAME (this_cmd);
		cmd2 = GLADE_COMMAND_SET_NAME (other_cmd);
762
763
764
765
766
767
768
769

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

	return FALSE;
}

static void
770
glade_command_set_name_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd)
771
{
772
773
	GladeCommandSetName *nthis = GLADE_COMMAND_SET_NAME (this_cmd);
	GladeCommandSetName *nother = GLADE_COMMAND_SET_NAME (other_cmd);
774

775
	g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (this_cmd) && GLADE_IS_COMMAND_SET_NAME (other_cmd));
776
777
778
779
780

	g_free (nthis->old_name);
	nthis->old_name = nother->old_name;
	nother->old_name = NULL;

781
782
	g_free (this_cmd->description);
	this_cmd->description = g_strdup_printf (_("Renaming %s to %s"), nthis->name, nthis->old_name);
783

Tristan Van Berkom's avatar
Updated    
Tristan Van Berkom committed
784
	glade_app_update_ui ();
785
786
787
788
789
790
791
792
}

/* this function takes the ownership of name */
void
glade_command_set_name (GladeWidget *widget, const gchar* name)
{
	GladeCommandSetName *me;
	GladeCommand *cmd;
793
794
795
796

	g_return_if_fail (GLADE_IS_WIDGET (widget));
	g_return_if_fail (name != NULL);

797
798
799
800
801
	/* Dont spam the queue with false name changes.
	 */
	if (!strcmp (widget->name, name))
		return;

802
803
	me = g_object_new (GLADE_COMMAND_SET_NAME_TYPE, NULL);
	cmd = GLADE_COMMAND (me);
804

805
806
807
808
809
	me->widget = widget;
	me->name = g_strdup (name);
	me->old_name = g_strdup (widget->name);

	cmd->description = g_strdup_printf (_("Renaming %s to %s"), me->old_name, me->name);
810
811

	glade_command_check_group (GLADE_COMMAND (me));
812

813
	if (glade_command_set_name_execute (GLADE_COMMAND (me)))
814
		glade_project_push_undo (GLADE_PROJECT (widget->project), GLADE_COMMAND (me));
815
816
	else
		g_object_unref (G_OBJECT (me));
817
818
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
819
820
821
822
823
824
825
/******************************************************************************
 * 
 * add/remove
 * 
 * These canonical commands add/remove a widget list to/from the project.
 * 
 *****************************************************************************/
826
827

typedef struct {
Tristan Van Berkom's avatar
Tristan Van Berkom committed
828
829
	GladeCommand	parent;
	GladeProject	*project;
830
831
	GList		*widgets;
	gboolean	add;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
832
833
	gboolean        from_clipboard;
} GladeCommandAddRemove;
834

Tristan Van Berkom's avatar
Tristan Van Berkom committed
835
836
837
838
839
840
841

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

843
844
845
846
847
848
849
850
851
852
static void 
glade_command_placeholder_destroyed (GtkObject *object, CommandData *cdata)
{
	if (GTK_OBJECT (cdata->placeholder) == object)
	{
		cdata->placeholder = NULL;
		cdata->handler_id = 0;
	}
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
853
854
855
856
857
858
static void
glade_command_placeholder_connect (CommandData *cdata,
				   GladePlaceholder *placeholder)
{
	g_assert (cdata && cdata->placeholder == NULL);

859
860
861
862
	/* Something like a no-op with no placeholder */
	if ((cdata->placeholder = placeholder) == NULL)
		return;

Tristan Van Berkom's avatar
Tristan Van Berkom committed
863
864
865
866
867
	cdata->handler_id = g_signal_connect 
		(placeholder, "destroy",
		 G_CALLBACK (glade_command_placeholder_destroyed), cdata);
}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
868
869
870
871
872
/**
 * glade_command_add:
 * @widgets: a #Glist
 * @parent: a #GladeWidget
 * @placeholder: a #GladePlaceholder
873
 * @pasting: whether we are pasting an existing widget or creating a new one.
Tristan Van Berkom's avatar
Tristan Van Berkom committed
874
875
876
877
 *
 * 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).
878
879
880
 * 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
881
 */
882
static void
883
884
885
886
glade_command_add (GList            *widgets, 
		   GladeWidget      *parent, 
		   GladePlaceholder *placeholder,
		   gboolean          pasting)
887
{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
888
889
890
891
892
	GladeCommandAddRemove	*me;
	CommandData				*cdata;
	GladeWidget				*widget = NULL;
	GList					*l, *list, *children, *placeholders = NULL;
	GtkWidget				*child;
893

Tristan Van Berkom's avatar
Tristan Van Berkom committed
894
895
	g_return_if_fail (widgets && widgets->data);
	g_return_if_fail (parent == NULL || GLADE_IS_WIDGET (parent));
896

Tristan Van Berkom's avatar
Tristan Van Berkom committed
897
898
	me = g_object_new (GLADE_COMMAND_ADD_REMOVE_TYPE, NULL);
	me->add = TRUE;
899
	me->from_clipboard = pasting;
900

Tristan Van Berkom's avatar
Tristan Van Berkom committed
901
902
903
904
905
906
	/* 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.
	 */
	widget = GLADE_WIDGET (widgets->data);
	if (placeholder && GTK_IS_WINDOW (widget->object) == FALSE)
907
	{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
908
909
		GladeWidget *some_widget = glade_placeholder_get_parent (placeholder);
		me->project = glade_widget_get_project (some_widget);
910
	}
Tristan Van Berkom's avatar
Tristan Van Berkom committed
911
	else me->project = glade_app_get_project();
912

Tristan Van Berkom's avatar
Tristan Van Berkom committed
913
914
915
	GLADE_COMMAND (me)->description = 
		g_strdup_printf (_("Add %s"), g_list_length (widgets) == 1 ? 
				 widget->name : _("multiple"));
916

Tristan Van Berkom's avatar
Tristan Van Berkom committed
917
	for (list = widgets; list && list->data; list = list->next)
918
	{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
		widget = list->data;
		cdata = g_new0 (CommandData, 1);
		if (widget->internal)
			g_critical ("Internal widget in Add");

		/* Widget */
		cdata->widget = g_object_ref (G_OBJECT (widget));
		
		/* Parent */
		if (parent == NULL)
			cdata->parent = glade_widget_get_parent (widget);
		else if (placeholder && GTK_IS_WINDOW (widget->object) == FALSE)
			cdata->parent = glade_placeholder_get_parent (placeholder);
		else if (GTK_IS_WINDOW (widget->object) == FALSE)
			cdata->parent = parent;
		if (cdata->parent == NULL && GTK_IS_WINDOW (widget->object) == FALSE)
			g_critical ("Parentless non GtkWindow widget in Add");
936

Tristan Van Berkom's avatar
Tristan Van Berkom committed
937
938
		/* Placeholder */
		if (placeholder != NULL && g_list_length (widgets) == 1)
939
		{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
940
			glade_command_placeholder_connect (cdata, placeholder);
Paolo Borelli's avatar
Paolo Borelli committed
941
		}
942
943
		else if (cdata->parent && 
			 glade_widget_placeholder_relation (cdata->parent, widget))
Tristan Van Berkom's avatar
Tristan Van Berkom committed
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
		{
			GtkContainer *cont = GTK_CONTAINER (cdata->parent->object);
			
			child = glade_util_get_placeholder_from_pointer (cont);
			if (child && g_list_find (placeholders, child) == NULL)
			{
				placeholders = g_list_append (placeholders, child);
				glade_command_placeholder_connect
						(cdata, GLADE_PLACEHOLDER (child));
			}
			else if ((children = glade_widget_adaptor_get_children
				 (cdata->parent->adaptor, cdata->parent->object)) != NULL)
			{
				for (l = children; l && l->data; l = l->next)
				{
					child = l->data;
Paolo Borelli's avatar
Paolo Borelli committed
960

Tristan Van Berkom's avatar
Tristan Van Berkom committed
961
962
963
964
965
966
967
968
969
970
971
972
973
					/* Find a placeholder for this child */
					if (GLADE_IS_PLACEHOLDER (child) &&
					    g_list_find (placeholders, child) == NULL)
					{
						placeholders = g_list_append (placeholders, child);
						glade_command_placeholder_connect
							(cdata, GLADE_PLACEHOLDER (child));
						break;
					}
				}
				g_list_free (children);
			}
		}
974

Tristan Van Berkom's avatar
Tristan Van Berkom committed
975
976
977
978
979
980
		/* 
		 * Save a copy of the original project so we can forward that to glade-project, 
		 * who'll copy in any resource files needed by any properties that are getting 
		 * cross-project pasted.
		 */
		cdata->project = cdata->widget->project;
981

Tristan Van Berkom's avatar
Tristan Van Berkom committed
982
		me->widgets = g_list_prepend (me->widgets, cdata);
983
984
	}

Tristan Van Berkom's avatar
Tristan Van Berkom committed
985
	glade_command_check_group (GLADE_COMMAND (me));
986

Tristan Van Berkom's avatar
Tristan Van Berkom committed
987
988
989
990
991
	/*
	 * Push it onto the undo stack only on success
	 */
	if (glade_command_add_remove_execute (GLADE_COMMAND (me)))
		glade_project_push_undo (glade_app_get_project(), GLADE_COMMAND (me));
992
	else
Tristan Van Berkom's avatar
Tristan Van Berkom committed
993
		g_object_unref (G_OBJECT (me));
994

Tristan Van Berkom's avatar
Tristan Van Berkom committed
995
996
	if (placeholders) 
		g_list_free (placeholders);
997
		
Tristan Van Berkom's avatar
Tristan Van Berkom committed
998
} /* end of glade_command_add() */
999

1000
/**
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1001
 * glade_command_remove:
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1002
 * @widgets: a #GList of #GladeWidgets
1003
 * @return_placeholders: whether or not to return a list of placehodlers
1004
 *
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1005
 * Performs a remove command on all widgets in @widgets from @parent.
1006
 */
1007
static void
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1008
glade_command_remove (GList *widgets)
1009
{
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1010
1011
1012
	GladeCommandAddRemove	*me;
	GladeWidget				*widget = NULL;
	CommandData				*cdata;
1013
	GtkWidget                               *placeholder;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1014
	GList					*list, *l;
1015

1016
1017
	g_return_if_fail (widgets != NULL);

Tristan Van Berkom's avatar
Tristan Van Berkom committed
1018
1019
 	me = g_object_new (GLADE_COMMAND_ADD_REMOVE_TYPE, NULL);
	me->add = FALSE;
1020
	me->from_clipboard = FALSE;
1021

1022
	/* internal children cannot be deleted. Notify the user. */
1023
	for (list = widgets; list && list->data; list = list->next)
1024
	{
1025
1026
1027
		widget = list->data;
		if (widget->internal)
		{
1028
1029
			glade_util_ui_message (glade_app_get_window(),	
				       GLADE_UI_WARN,
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1030
					       _("You cannot remove a widget internal to a composite widget."));
1031
1032
1033
1034
			return;
		}
	}

1035
1036
	me->project = glade_widget_get_project (widget);

1037
1038
1039
1040
1041
1042
1043
	for (list = widgets; list && list->data; list = list->next)
	{
		widget         = list->data;

		cdata          = g_new0 (CommandData, 1);
		cdata->widget  = g_object_ref (G_OBJECT (widget));
		cdata->parent  = glade_widget_get_parent (widget);
1044
		cdata->project = glade_widget_get_project (widget);
1045

1046
		if (widget->internal)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1047
			g_critical ("Internal widget in Remove");
1048

1049
		if (cdata->parent != NULL &&
1050
		    glade_widget_placeholder_relation 
1051
1052
		    (cdata->parent, cdata->widget))
		{
1053
1054
1055
			placeholder = glade_placeholder_new ();
			glade_command_placeholder_connect
				(cdata, GLADE_PLACEHOLDER (placeholder));
1056
1057
		}
		me->widgets = g_list_prepend (me->widgets, cdata);
1058

1059
1060
1061
1062
		/* Dont record props in create_execute (whether or not we actually
		 * record any props here
		 */
		cdata->props_recorded = TRUE; 
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072

		/* Record packing props if not deleted from the clipboard */
		if (me->from_clipboard == FALSE)
		{
			for (l = widget->packing_properties; l; l = l->next)
				cdata->pack_props = 
					g_list_prepend (cdata->pack_props,
							glade_property_dup (GLADE_PROPERTY (l->data),
									    cdata->widget));
		}
1073
	}
1074

1075
1076
	if (g_list_length (widgets) == 1)
		GLADE_COMMAND (me)->description =
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1077
			g_strdup_printf (_("Remove %s"), 
1078
1079
1080
					 GLADE_WIDGET (widgets->data)->name);
	else
		GLADE_COMMAND (me)->description =
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1081
			g_strdup_printf (_("Remove multiple"));
1082

1083
	g_assert (widget);
1084
1085
1086

	glade_command_check_group (GLADE_COMMAND (me));

Tristan Van Berkom's avatar
Tristan Van Berkom committed
1087
1088
	if (glade_command_add_remove_execute (GLADE_COMMAND (me)))
		glade_project_push_undo (GLADE_PROJECT (widget->project), GLADE_COMMAND (me));
1089
1090
	else
		g_object_unref (G_OBJECT (me));
1091

Tristan Van Berkom's avatar
Tristan Van Berkom committed
1092
} /* end of glade_command_remove() */
1093

1094
1095
1096
1097
1098
1099
1100
1101
1102
static void
glade_command_transfer_props (GladeWidget *gnew, GList *saved_props)
{
	GList *l;

	for (l = saved_props; l; l = l->next)
	{
		GladeProperty *prop, *sprop = l->data;
		
1103
		prop = glade_widget_get_pack_property (gnew, sprop->klass->id);
1104

1105
1106
		if (prop && sprop->klass->transfer_on_paste &&
		    glade_property_class_match (prop->klass, sprop->klass))
1107
1108
1109
1110
			glade_property_set_value (prop, sprop->value);
	}
}

1111
static gboolean
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1112
glade_command_add_execute (GladeCommandAddRemove *me)
1113
{
Tristan Van Berkom's avatar
Updated    
Tristan Van Berkom committed
1114
	GladeProject       *active_project = glade_app_get_project ();
1115
	CommandData        *cdata;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
1116
	GList              *list, *l, *saved_props;
1117