1 /*
2  * Copyright (C) 2002 Joaquín Cuenca Abela
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /**
27  * SECTION:glade-command
28  * @Short_Description: An event filter to implement the Undo/Redo stack.
29  *
30  * The Glade Command api allows us to view user actions as items and execute
31  * and undo those items; each #GladeProject has its own Undo/Redo stack.
32  */
33 
34 #include <gtk/gtk.h>
35 #include <string.h>
36 #include <glib/gi18n-lib.h>
37 #include "glade.h"
38 #include "glade-project.h"
39 #include "glade-xml-utils.h"
40 #include "glade-widget.h"
41 #include "glade-palette.h"
42 #include "glade-command.h"
43 #include "glade-property.h"
44 #include "glade-property-class.h"
45 #include "glade-debug.h"
46 #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"
51 
52 struct _GladeCommandPrivate
53 {
54   GladeProject *project; /* The project this command is created for */
55 
56   gchar *description; /* a string describing the command.
57 		       * It's used in the undo/redo menu entry.
58 		       */
59 
60   gint   group_id;    /* If this is part of a command group, this is
61 		       * the group id (id is needed only to ensure that
62 		       * consecutive groups dont get merged).
63 		       */
64 };
65 
66 /* Concerning placeholders: we do not hold any reference to placeholders,
67  * placeholders that are supplied by the backend are not reffed, placeholders
68  * that are created by glade-command are temporarily owned by glade-command
69  * untill they are added to a container; in which case it belongs to GTK+
70  * and the backend (but I prefer to think of it as the backend).
71  */
72 typedef struct
73 {
74   GladeWidget *widget;
75   GladeWidget *parent;
76   GList *reffed;
77   GladePlaceholder *placeholder;
78   gboolean props_recorded;
79   GList *pack_props;
80   gchar *special_type;
81   gulong handler_id;
82 } CommandData;
83 
84 /* Group description used for the current group
85  */
86 static gchar *gc_group_description = NULL;
87 
88 /* Use an id to avoid grouping together consecutive groups
89  */
90 static gint gc_group_id = 1;
91 
92 /* Groups can be grouped together, push/pop must balance (duh!)
93  */
94 static gint gc_group_depth = 0;
95 
96 
G_DEFINE_TYPE_WITH_PRIVATE(GladeCommand,glade_command,G_TYPE_OBJECT)97 G_DEFINE_TYPE_WITH_PRIVATE (GladeCommand, glade_command, G_TYPE_OBJECT)
98 
99 static void
100 glade_command_finalize (GObject *obj)
101 {
102   GladeCommand *cmd = (GladeCommand *) obj;
103   g_return_if_fail (cmd != NULL);
104 
105   if (cmd->priv->description)
106     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
glade_command_unifies_impl(GladeCommand * this_cmd,GladeCommand * other_cmd)113 glade_command_unifies_impl (GladeCommand *this_cmd, GladeCommand *other_cmd)
114 {
115   return FALSE;
116 }
117 
118 static void
glade_command_collapse_impl(GladeCommand * this_cmd,GladeCommand * other_cmd)119 glade_command_collapse_impl (GladeCommand *this_cmd, GladeCommand *other_cmd)
120 {
121   g_return_if_reached ();
122 }
123 
124 static void
glade_command_init(GladeCommand * command)125 glade_command_init (GladeCommand *command)
126 {
127   command->priv = glade_command_get_instance_private (command);
128 }
129 
130 static void
glade_command_class_init(GladeCommandClass * klass)131 glade_command_class_init (GladeCommandClass *klass)
132 {
133   GObjectClass *object_class;
134 
135   object_class = G_OBJECT_CLASS (klass);
136 
137   object_class->finalize = glade_command_finalize;
138 
139   klass->undo = NULL;
140   klass->execute = NULL;
141   klass->unifies = glade_command_unifies_impl;
142   klass->collapse = glade_command_collapse_impl;
143 }
144 
145 /* Macros for defining the derived command types */
146 #define MAKE_TYPE(func, type, parent)			\
147 GType							\
148 func ## _get_type (void)				\
149 {							\
150 	static GType cmd_type = 0;			\
151 							\
152 	if (!cmd_type)					\
153 	{						\
154 		static const GTypeInfo info =		\
155 		{					\
156 			sizeof (type ## Class),		\
157 			(GBaseInitFunc) NULL,		\
158 			(GBaseFinalizeFunc) NULL,	\
159 			(GClassInitFunc) func ## _class_init,	\
160 			(GClassFinalizeFunc) NULL,	\
161 			NULL,				\
162 			sizeof (type),			\
163 			0,				\
164 			(GInstanceInitFunc) NULL	\
165 		};					\
166 							\
167 		cmd_type = g_type_register_static (parent, #type, &info, 0);	\
168 	}						\
169 							\
170 	return cmd_type;				\
171 }							\
172 
173 #define GLADE_MAKE_COMMAND(type, func)					\
174 static gboolean								\
175 func ## _undo (GladeCommand *me);					\
176 static gboolean								\
177 func ## _execute (GladeCommand *me);					\
178 static void								\
179 func ## _finalize (GObject *object);					\
180 static gboolean								\
181 func ## _unifies (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
182 static void								\
183 func ## _collapse (GladeCommand *this_cmd, GladeCommand *other_cmd);		\
184 static void								\
185 func ## _class_init (gpointer parent_tmp, gpointer notused)		\
186 {									\
187 	GladeCommandClass *parent = parent_tmp;				\
188 	GObjectClass* object_class;					\
189 	object_class = G_OBJECT_CLASS (parent);				\
190 	parent->undo =  func ## _undo;					\
191 	parent->execute =  func ## _execute;				\
192 	parent->unifies =  func ## _unifies;				\
193 	parent->collapse =  func ## _collapse;				\
194 	object_class->finalize = func ## _finalize;			\
195 }									\
196 typedef struct {							\
197 	GladeCommandClass cmd;						\
198 } type ## Class;							\
199 static MAKE_TYPE(func, type, GLADE_TYPE_COMMAND)
200 
201 
202 G_CONST_RETURN gchar *
glade_command_description(GladeCommand * command)203 glade_command_description (GladeCommand *command)
204 {
205   g_return_val_if_fail (GLADE_IS_COMMAND (command), NULL);
206 
207   return command->priv->description;
208 }
209 
210 gint
glade_command_group_id(GladeCommand * command)211 glade_command_group_id (GladeCommand *command)
212 {
213   g_return_val_if_fail (GLADE_IS_COMMAND (command), -1);
214 
215   return command->priv->group_id;
216 }
217 
218 
219 /**
220  * glade_command_execute:
221  * @command: A #GladeCommand
222  *
223  * Executes @command
224  *
225  * Returns: whether the command was successfully executed
226  */
227 gboolean
glade_command_execute(GladeCommand * command)228 glade_command_execute (GladeCommand *command)
229 {
230   g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
231   return GLADE_COMMAND_GET_CLASS (command)->execute (command);
232 }
233 
234 
235 /**
236  * glade_command_undo:
237  * @command: A #GladeCommand
238  *
239  * Undo the effects of @command
240  *
241  * Returns: whether the command was successfully reversed
242  */
243 gboolean
glade_command_undo(GladeCommand * command)244 glade_command_undo (GladeCommand *command)
245 {
246   g_return_val_if_fail (GLADE_IS_COMMAND (command), FALSE);
247   return GLADE_COMMAND_GET_CLASS (command)->undo (command);
248 }
249 
250 /**
251  * glade_command_unifies:
252  * @command: A #GladeCommand
253  * @other: another #GladeCommand
254  *
255  * Checks whether @command and @other can be unified
256  * to make one single command.
257  *
258  * Returns: whether they can be unified.
259  */
260 gboolean
glade_command_unifies(GladeCommand * command,GladeCommand * other)261 glade_command_unifies (GladeCommand *command, GladeCommand *other)
262 {
263   g_return_val_if_fail (command, FALSE);
264 
265   /* Cannot unify with a part of a command group.
266    * Unify atomic commands only
267    */
268   if (command->priv->group_id != 0 || (other && other->priv->group_id != 0))
269     return FALSE;
270 
271   return GLADE_COMMAND_GET_CLASS (command)->unifies (command, other);
272 }
273 
274 /**
275  * glade_command_collapse:
276  * @command: A #GladeCommand
277  * @other: another #GladeCommand
278  *
279  * Merges @other into @command, so that @command now
280  * covers both commands and @other can be dispensed with.
281  */
282 void
glade_command_collapse(GladeCommand * command,GladeCommand * other)283 glade_command_collapse (GladeCommand *command, GladeCommand *other)
284 {
285   g_return_if_fail (command);
286   GLADE_COMMAND_GET_CLASS (command)->collapse (command, other);
287 }
288 
289 /**
290  * glade_command_push_group:
291  * @fmt:         The collective desctiption of the command group.
292  *               only the description of the first group on the
293  *               stack is used when embedding groups.
294  * @...: args to the format string.
295  *
296  * Marks the begining of a group.
297  */
298 void
glade_command_push_group(const gchar * fmt,...)299 glade_command_push_group (const gchar *fmt, ...)
300 {
301   va_list args;
302 
303   g_return_if_fail (fmt);
304 
305   /* Only use the description for the first group.
306    */
307   if (gc_group_depth++ == 0)
308     {
309       va_start (args, fmt);
310       gc_group_description = g_strdup_vprintf (fmt, args);
311       va_end (args);
312     }
313 }
314 
315 /**
316  * glade_command_pop_group:
317  *
318  * Mark the end of a command group.
319  */
320 void
glade_command_pop_group(void)321 glade_command_pop_group (void)
322 {
323   if (gc_group_depth-- == 1)
324     {
325       gc_group_description = (g_free (gc_group_description), NULL);
326       gc_group_id++;
327     }
328 
329   if (gc_group_depth < 0)
330     g_critical ("Unbalanced group stack detected in %s\n", G_STRFUNC);
331 }
332 
333 gint
glade_command_get_group_depth(void)334 glade_command_get_group_depth (void)
335 {
336   return gc_group_depth;
337 }
338 
339 static void
glade_command_check_group(GladeCommand * cmd)340 glade_command_check_group (GladeCommand *cmd)
341 {
342   g_return_if_fail (GLADE_IS_COMMAND (cmd));
343   if (gc_group_description)
344     {
345       cmd->priv->description =
346           (g_free (cmd->priv->description), g_strdup (gc_group_description));
347       cmd->priv->group_id = gc_group_id;
348     }
349 }
350 
351 
352 /****************************************************/
353 /*******  GLADE_COMMAND_PROPERTY_ENABLED      *******/
354 /****************************************************/
355 typedef struct
356 {
357   GladeCommand parent;
358   GladeProperty *property;
359   gboolean old_enabled;
360   gboolean new_enabled;
361 } GladeCommandPropertyEnabled;
362 
363 /* standard macros */
364 GLADE_MAKE_COMMAND (GladeCommandPropertyEnabled, glade_command_property_enabled);
365 #define GLADE_COMMAND_PROPERTY_ENABLED_TYPE       (glade_command_property_enabled_get_type ())
366 #define GLADE_COMMAND_PROPERTY_ENABLED(o)	  (G_TYPE_CHECK_INSTANCE_CAST \
367 						   ((o), GLADE_COMMAND_PROPERTY_ENABLED_TYPE, \
368 						    GladeCommandPropertyEnabled))
369 #define GLADE_COMMAND_PROPERTY_ENABLED_CLASS(k)	  (G_TYPE_CHECK_CLASS_CAST \
370 						   ((k), GLADE_COMMAND_PROPERTY_ENABLED_TYPE, \
371 						    GladeCommandPropertyEnabledClass))
372 #define GLADE_IS_COMMAND_PROPERTY_ENABLED(o)	   (G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_PROPERTY_ENABLED_TYPE))
373 #define GLADE_IS_COMMAND_PROPERTY_ENABLED_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_PROPERTY_ENABLED_TYPE))
374 
375 
376 static gboolean
glade_command_property_enabled_execute(GladeCommand * cmd)377 glade_command_property_enabled_execute (GladeCommand *cmd)
378 {
379   GladeCommandPropertyEnabled *me = GLADE_COMMAND_PROPERTY_ENABLED (cmd);
380 
381   glade_property_set_enabled (me->property, me->new_enabled);
382 
383   return TRUE;
384 }
385 
386 static gboolean
glade_command_property_enabled_undo(GladeCommand * cmd)387 glade_command_property_enabled_undo (GladeCommand *cmd)
388 {
389   GladeCommandPropertyEnabled *me = GLADE_COMMAND_PROPERTY_ENABLED (cmd);
390 
391   glade_property_set_enabled (me->property, me->old_enabled);
392 
393   return TRUE;
394 }
395 
396 static void
glade_command_property_enabled_finalize(GObject * obj)397 glade_command_property_enabled_finalize (GObject *obj)
398 {
399   GladeCommandPropertyEnabled *me;
400 
401   g_return_if_fail (GLADE_IS_COMMAND_PROPERTY_ENABLED (obj));
402 
403   me = GLADE_COMMAND_PROPERTY_ENABLED (obj);
404 
405   g_object_unref (me->property);
406   glade_command_finalize (obj);
407 }
408 
409 static gboolean
glade_command_property_enabled_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)410 glade_command_property_enabled_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
411 {
412   GladeCommandPropertyEnabled *cmd1;
413   GladeCommandPropertyEnabled *cmd2;
414 
415   if (!other_cmd)
416     {
417       if (GLADE_IS_COMMAND_PROPERTY_ENABLED (this_cmd))
418         {
419           cmd1 = (GladeCommandPropertyEnabled *) this_cmd;
420 
421 	  return (cmd1->old_enabled == cmd1->new_enabled);
422         }
423       return FALSE;
424     }
425 
426   if (GLADE_IS_COMMAND_PROPERTY_ENABLED (this_cmd) &&
427       GLADE_IS_COMMAND_PROPERTY_ENABLED (other_cmd))
428     {
429       cmd1 = GLADE_COMMAND_PROPERTY_ENABLED (this_cmd);
430       cmd2 = GLADE_COMMAND_PROPERTY_ENABLED (other_cmd);
431 
432       return (cmd1->property == cmd2->property);
433     }
434 
435   return FALSE;
436 }
437 
438 static void
glade_command_property_enabled_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)439 glade_command_property_enabled_collapse (GladeCommand *this_cmd,
440 					 GladeCommand *other_cmd)
441 {
442   GladeCommandPropertyEnabled *this = GLADE_COMMAND_PROPERTY_ENABLED (this_cmd);
443   GladeCommandPropertyEnabled *other = GLADE_COMMAND_PROPERTY_ENABLED (other_cmd);
444   GladeWidget *widget;
445   GladePropertyClass *pclass;
446 
447   this->new_enabled = other->new_enabled;
448 
449   widget = glade_property_get_widget (this->property);
450   pclass = glade_property_get_class (this->property);
451 
452   g_free (this_cmd->priv->description);
453   if (this->new_enabled)
454     this_cmd->priv->description =
455       g_strdup_printf (_("Enabling property %s on widget %s"),
456 		       glade_property_class_get_name (pclass),
457 		       glade_widget_get_name (widget));
458   else
459     this_cmd->priv->description =
460       g_strdup_printf (_("Disabling property %s on widget %s"),
461 		       glade_property_class_get_name (pclass),
462 		       glade_widget_get_name (widget));
463 }
464 
465 /**
466  * glade_command_set_property_enabled:
467  * @property: An optional #GladeProperty
468  * @enabled: Whether the property should be enabled
469  *
470  * Enables or disables @property.
471  *
472  * @property must be an optional property.
473  */
474 void
glade_command_set_property_enabled(GladeProperty * property,gboolean enabled)475 glade_command_set_property_enabled (GladeProperty *property,
476 				    gboolean       enabled)
477 {
478   GladeCommandPropertyEnabled *me;
479   GladeCommand *cmd;
480   GladeWidget *widget;
481   GladePropertyClass *pclass;
482   gboolean old_enabled;
483 
484   /* Sanity checks */
485   g_return_if_fail (GLADE_IS_PROPERTY (property));
486 
487   widget = glade_property_get_widget (property);
488   g_return_if_fail (GLADE_IS_WIDGET (widget));
489 
490   /* Only applies to optional properties */
491   pclass = glade_property_get_class (property);
492   g_return_if_fail (glade_property_class_optional (pclass));
493 
494   /* Fetch current state */
495   old_enabled = glade_property_get_enabled (property);
496 
497   /* Avoid useless command */
498   if (old_enabled == enabled)
499     return;
500 
501   me = g_object_new (GLADE_COMMAND_PROPERTY_ENABLED_TYPE, NULL);
502   cmd = GLADE_COMMAND (me);
503   cmd->priv->project = glade_widget_get_project (widget);
504 
505   me->property = g_object_ref (property);
506   me->new_enabled = enabled;
507   me->old_enabled = old_enabled;
508 
509   if (enabled)
510     cmd->priv->description =
511       g_strdup_printf (_("Enabling property %s on widget %s"),
512 		       glade_property_class_get_name (pclass),
513 		       glade_widget_get_name (widget));
514   else
515     cmd->priv->description =
516       g_strdup_printf (_("Disabling property %s on widget %s"),
517 		       glade_property_class_get_name (pclass),
518 		       glade_widget_get_name (widget));
519 
520   glade_command_check_group (GLADE_COMMAND (me));
521 
522   if (glade_command_property_enabled_execute (GLADE_COMMAND (me)))
523     glade_project_push_undo (cmd->priv->project, cmd);
524   else
525     g_object_unref (G_OBJECT (me));
526 }
527 
528 /**************************************************/
529 /*******     GLADE_COMMAND_SET_PROPERTY     *******/
530 /**************************************************/
531 
532 /* create a new GladeCommandSetProperty class.  Objects of this class will
533  * encapsulate a "set property" operation */
534 
535 typedef struct
536 {
537   GladeCommand parent;
538   gboolean set_once;
539   gboolean undo;
540   GList *sdata;
541 } GladeCommandSetProperty;
542 
543 /* standard macros */
544 GLADE_MAKE_COMMAND (GladeCommandSetProperty, glade_command_set_property);
545 #define GLADE_COMMAND_SET_PROPERTY_TYPE		(glade_command_set_property_get_type ())
546 #define GLADE_COMMAND_SET_PROPERTY(o)	  	(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_PROPERTY_TYPE, GladeCommandSetProperty))
547 #define GLADE_COMMAND_SET_PROPERTY_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_PROPERTY_TYPE, GladeCommandSetPropertyClass))
548 #define GLADE_IS_COMMAND_SET_PROPERTY(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_PROPERTY_TYPE))
549 #define GLADE_IS_COMMAND_SET_PROPERTY_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_PROPERTY_TYPE))
550 
551 /* Undo the last "set property command" */
552 static gboolean
glade_command_set_property_undo(GladeCommand * cmd)553 glade_command_set_property_undo (GladeCommand *cmd)
554 {
555   return glade_command_set_property_execute (cmd);
556 }
557 
558 /*
559  * Execute the set property command and revert it. IE, after the execution of
560  * this function cmd will point to the undo action
561  */
562 static gboolean
glade_command_set_property_execute(GladeCommand * cmd)563 glade_command_set_property_execute (GladeCommand *cmd)
564 {
565   GladeCommandSetProperty *me = (GladeCommandSetProperty *) cmd;
566   GList *l;
567   gboolean success;
568   gboolean retval = FALSE;
569 
570   g_return_val_if_fail (me != NULL, FALSE);
571 
572   if (me->set_once != FALSE)
573     glade_property_push_superuser ();
574 
575   for (l = me->sdata; l; l = l->next)
576     {
577       GValue              new_value = { 0, };
578       GCSetPropData      *sdata = l->data;
579       GladePropertyClass *pclass = glade_property_get_class (sdata->property);
580       GladeWidget        *widget = glade_property_get_widget (sdata->property);
581 
582       g_value_init (&new_value, G_VALUE_TYPE (sdata->new_value));
583 
584       if (me->undo)
585         g_value_copy (sdata->old_value, &new_value);
586       else
587         g_value_copy (sdata->new_value, &new_value);
588 
589 #ifdef GLADE_ENABLE_DEBUG
590       if (glade_get_debug_flags () & GLADE_DEBUG_COMMANDS)
591 	{
592 	  gchar *str =
593             glade_widget_adaptor_string_from_value
594             (glade_property_class_get_adaptor (pclass), pclass, &new_value);
595 
596 	  g_print ("Setting %s property of %s to %s (sumode: %d)\n",
597 		   glade_property_class_id (pclass),
598 		   glade_widget_get_name (widget),
599 		   str, glade_property_superuser ());
600 
601 	  g_free (str);
602 	}
603 #endif
604 
605       /* Packing properties need to be refreshed here since
606        * they are reset when they get added to containers.
607        */
608       if (glade_property_class_get_is_packing (pclass))
609         {
610           GladeProperty *tmp_prop;
611 
612           tmp_prop = glade_widget_get_pack_property (widget, glade_property_class_id (pclass));
613 
614           if (sdata->property != tmp_prop)
615             {
616               g_object_unref (sdata->property);
617               sdata->property = g_object_ref (tmp_prop);
618             }
619         }
620 
621       success = glade_property_set_value (sdata->property, &new_value);
622       retval  = retval || success;
623 
624       if (!me->set_once && success)
625         {
626           /* If some verify functions didnt pass on
627            * the first go.. we need to record the actual
628            * properties here. XXX should be able to use glade_property_get_value() here
629            */
630           g_value_copy (glade_property_inline_value (sdata->property), sdata->new_value);
631         }
632 
633       g_value_unset (&new_value);
634     }
635 
636   if (me->set_once != FALSE)
637     glade_property_pop_superuser ();
638 
639   me->set_once = TRUE;
640   me->undo = !me->undo;
641 
642   return retval;
643 }
644 
645 static void
glade_command_set_property_finalize(GObject * obj)646 glade_command_set_property_finalize (GObject *obj)
647 {
648   GladeCommandSetProperty *me;
649   GList *l;
650 
651   me = GLADE_COMMAND_SET_PROPERTY (obj);
652 
653   for (l = me->sdata; l; l = l->next)
654     {
655       GCSetPropData *sdata = l->data;
656 
657       if (sdata->property)
658         g_object_unref (G_OBJECT (sdata->property));
659 
660       if (sdata->old_value)
661         {
662           if (G_VALUE_TYPE (sdata->old_value) != 0)
663             g_value_unset (sdata->old_value);
664           g_free (sdata->old_value);
665         }
666       if (G_VALUE_TYPE (sdata->new_value) != 0)
667         g_value_unset (sdata->new_value);
668       g_free (sdata->new_value);
669 
670     }
671   glade_command_finalize (obj);
672 }
673 
674 static gboolean
glade_command_set_property_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)675 glade_command_set_property_unifies (GladeCommand *this_cmd,
676                                     GladeCommand *other_cmd)
677 {
678   GladeCommandSetProperty *cmd1, *cmd2;
679   GladePropertyClass *pclass1, *pclass2;
680   GCSetPropData *pdata1, *pdata2;
681   GladeWidget *widget1, *widget2;
682   GList *list, *l;
683 
684   if (!other_cmd)
685     {
686       if (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd))
687         {
688           cmd1 = (GladeCommandSetProperty *) this_cmd;
689 
690           for (list = cmd1->sdata; list; list = list->next)
691             {
692               pdata1  = list->data;
693 	      pclass1 = glade_property_get_class (pdata1->property);
694 
695               if (glade_property_class_compare (pclass1,
696                                                 pdata1->old_value,
697                                                 pdata1->new_value))
698                 return FALSE;
699             }
700           return TRUE;
701 
702         }
703       return FALSE;
704     }
705 
706   if (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
707       GLADE_IS_COMMAND_SET_PROPERTY (other_cmd))
708     {
709       cmd1 = (GladeCommandSetProperty *) this_cmd;
710       cmd2 = (GladeCommandSetProperty *) other_cmd;
711 
712       if (g_list_length (cmd1->sdata) != g_list_length (cmd2->sdata))
713         return FALSE;
714 
715       for (list = cmd1->sdata; list; list = list->next)
716         {
717           pdata1  = list->data;
718 	  pclass1 = glade_property_get_class (pdata1->property);
719 	  widget1 = glade_property_get_widget (pdata1->property);
720 
721           for (l = cmd2->sdata; l; l = l->next)
722             {
723               pdata2  = l->data;
724 	      pclass2 = glade_property_get_class (pdata2->property);
725 	      widget2 = glade_property_get_widget (pdata2->property);
726 
727               if (widget1 == widget2 &&
728                   glade_property_class_match (pclass1, pclass2))
729                 break;
730             }
731 
732           /* If both lists are the same length, and one class type
733            * is not found in the other list, then we cant unify.
734            */
735           if (l == NULL)
736             return FALSE;
737         }
738 
739       return TRUE;
740     }
741   return FALSE;
742 }
743 
744 static void
glade_command_set_property_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)745 glade_command_set_property_collapse (GladeCommand *this_cmd,
746                                      GladeCommand *other_cmd)
747 {
748   GladeCommandSetProperty *cmd1, *cmd2;
749   GCSetPropData *pdata1, *pdata2;
750   GladePropertyClass *pclass1, *pclass2;
751   GList *list, *l;
752 
753   g_return_if_fail (GLADE_IS_COMMAND_SET_PROPERTY (this_cmd) &&
754                     GLADE_IS_COMMAND_SET_PROPERTY (other_cmd));
755 
756   cmd1 = (GladeCommandSetProperty *) this_cmd;
757   cmd2 = (GladeCommandSetProperty *) other_cmd;
758 
759 
760   for (list = cmd1->sdata; list; list = list->next)
761     {
762       pdata1  = list->data;
763       pclass1 = glade_property_get_class (pdata1->property);
764 
765       for (l = cmd2->sdata; l; l = l->next)
766         {
767           pdata2  = l->data;
768 	  pclass2 = glade_property_get_class (pdata2->property);
769 
770           if (glade_property_class_match (pclass1, pclass2))
771             {
772               /* Merge the GCSetPropData structs manually here
773                */
774               g_value_copy (pdata2->new_value, pdata1->new_value);
775               break;
776             }
777         }
778     }
779 
780   /* Set the description
781    */
782   g_free (this_cmd->priv->description);
783   this_cmd->priv->description = other_cmd->priv->description;
784   other_cmd->priv->description = NULL;
785 }
786 
787 
788 #define MAX_UNDO_MENU_ITEM_VALUE_LEN	10
789 static gchar *
glade_command_set_property_description(GladeCommandSetProperty * me)790 glade_command_set_property_description (GladeCommandSetProperty *me)
791 {
792   GCSetPropData *sdata;
793   gchar *description = NULL;
794   gchar *value_name;
795   GladePropertyClass *pclass;
796   GladeWidget *widget;
797 
798   g_assert (me->sdata);
799 
800   if (g_list_length (me->sdata) > 1)
801     description = g_strdup_printf (_("Setting multiple properties"));
802   else
803     {
804       sdata  = me->sdata->data;
805       pclass = glade_property_get_class (sdata->property);
806       widget = glade_property_get_widget (sdata->property);
807       value_name = glade_widget_adaptor_string_from_value
808 	(glade_property_class_get_adaptor (pclass), pclass, sdata->new_value);
809 
810       if (!value_name || strlen (value_name) > MAX_UNDO_MENU_ITEM_VALUE_LEN
811           || strchr (value_name, '_'))
812         {
813           description = g_strdup_printf (_("Setting %s of %s"),
814                                          glade_property_class_get_name (pclass),
815                                          glade_widget_get_name (widget));
816         }
817       else
818         {
819           description = g_strdup_printf (_("Setting %s of %s to %s"),
820                                          glade_property_class_get_name (pclass),
821                                          glade_widget_get_name (widget),
822                                          value_name);
823         }
824       g_free (value_name);
825     }
826 
827   return description;
828 }
829 
830 /**
831  * glade_command_set_properties_list:
832  *
833  * @props (element-type GladeProperty): List of #GladeProperty
834  */
835 void
glade_command_set_properties_list(GladeProject * project,GList * props)836 glade_command_set_properties_list (GladeProject *project, GList *props)
837 {
838   GladeCommandSetProperty *me;
839   GladeCommand *cmd;
840   GCSetPropData *sdata;
841   GList *list;
842   gboolean success;
843   gboolean multiple;
844 
845   g_return_if_fail (GLADE_IS_PROJECT (project));
846   g_return_if_fail (props);
847 
848   me = (GladeCommandSetProperty *)
849       g_object_new (GLADE_COMMAND_SET_PROPERTY_TYPE, NULL);
850   cmd = GLADE_COMMAND (me);
851   cmd->priv->project = project;
852 
853   /* Ref all props */
854   for (list = props; list; list = list->next)
855     {
856       sdata = list->data;
857       g_object_ref (G_OBJECT (sdata->property));
858     }
859 
860   me->sdata = props;
861   cmd->priv->description = glade_command_set_property_description (me);
862 
863   multiple = g_list_length (me->sdata) > 1;
864   if (multiple)
865     glade_command_push_group ("%s", cmd->priv->description);
866 
867   glade_command_check_group (GLADE_COMMAND (me));
868 
869   /* Push onto undo stack only if it executes successfully. */
870   success = glade_command_set_property_execute (cmd);
871 
872   if (success)
873     glade_project_push_undo (cmd->priv->project, cmd);
874   else
875     g_object_unref (G_OBJECT (me));
876 
877   if (multiple)
878     glade_command_pop_group ();
879 }
880 
881 
882 void
glade_command_set_properties(GladeProperty * property,const GValue * old_value,const GValue * new_value,...)883 glade_command_set_properties (GladeProperty *property,
884                               const GValue *old_value,
885                               const GValue *new_value,
886                               ...)
887 {
888   GCSetPropData *sdata;
889   GladeProperty *prop;
890   GladeWidget   *widget;
891   GladeProject  *project;
892   GValue *ovalue, *nvalue;
893   GList *list = NULL;
894   va_list vl;
895 
896   g_return_if_fail (GLADE_IS_PROPERTY (property));
897 
898   /* Add first set */
899   sdata = g_new (GCSetPropData, 1);
900   sdata->property = property;
901   sdata->old_value = g_new0 (GValue, 1);
902   sdata->new_value = g_new0 (GValue, 1);
903   g_value_init (sdata->old_value, G_VALUE_TYPE (old_value));
904   g_value_init (sdata->new_value, G_VALUE_TYPE (new_value));
905   g_value_copy (old_value, sdata->old_value);
906   g_value_copy (new_value, sdata->new_value);
907   list = g_list_prepend (list, sdata);
908 
909   va_start (vl, new_value);
910   while ((prop = va_arg (vl, GladeProperty *)) != NULL)
911     {
912       ovalue = va_arg (vl, GValue *);
913       nvalue = va_arg (vl, GValue *);
914 
915       g_assert (GLADE_IS_PROPERTY (prop));
916       g_assert (G_IS_VALUE (ovalue));
917       g_assert (G_IS_VALUE (nvalue));
918 
919       sdata = g_new (GCSetPropData, 1);
920       sdata->property = g_object_ref (G_OBJECT (prop));
921       sdata->old_value = g_new0 (GValue, 1);
922       sdata->new_value = g_new0 (GValue, 1);
923       g_value_init (sdata->old_value, G_VALUE_TYPE (ovalue));
924       g_value_init (sdata->new_value, G_VALUE_TYPE (nvalue));
925       g_value_copy (ovalue, sdata->old_value);
926       g_value_copy (nvalue, sdata->new_value);
927       list = g_list_prepend (list, sdata);
928     }
929   va_end (vl);
930 
931   widget  = glade_property_get_widget (property);
932   project = glade_widget_get_project (widget);
933   glade_command_set_properties_list (project, list);
934 }
935 
936 void
glade_command_set_property_value(GladeProperty * property,const GValue * pvalue)937 glade_command_set_property_value (GladeProperty *property, const GValue *pvalue)
938 {
939 
940   /* Dont generate undo/redo items for unchanging property values.
941    */
942   if (glade_property_equals_value (property, pvalue))
943     return;
944 
945   glade_command_set_properties (property, glade_property_inline_value (property), pvalue, NULL);
946 }
947 
948 void
glade_command_set_property(GladeProperty * property,...)949 glade_command_set_property (GladeProperty * property, ...)
950 {
951   GValue *value;
952   va_list args;
953 
954   g_return_if_fail (GLADE_IS_PROPERTY (property));
955 
956   va_start (args, property);
957   value = glade_property_class_make_gvalue_from_vl (glade_property_get_class (property), args);
958   va_end (args);
959 
960   glade_command_set_property_value (property, value);
961 }
962 
963 /**************************************************/
964 /*******       GLADE_COMMAND_SET_NAME       *******/
965 /**************************************************/
966 
967 /* create a new GladeCommandSetName class.  Objects of this class will
968  * encapsulate a "set name" operation */
969 typedef struct
970 {
971   GladeCommand parent;
972 
973   GladeWidget *widget;
974   gchar *old_name;
975   gchar *name;
976 } GladeCommandSetName;
977 
978 /* standard macros */
979 GLADE_MAKE_COMMAND (GladeCommandSetName, glade_command_set_name);
980 #define GLADE_COMMAND_SET_NAME_TYPE		(glade_command_set_name_get_type ())
981 #define GLADE_COMMAND_SET_NAME(o)	  	(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_NAME_TYPE, GladeCommandSetName))
982 #define GLADE_COMMAND_SET_NAME_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_NAME_TYPE, GladeCommandSetNameClass))
983 #define GLADE_IS_COMMAND_SET_NAME(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_NAME_TYPE))
984 #define GLADE_IS_COMMAND_SET_NAME_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_NAME_TYPE))
985 
986 /* Undo the last "set name command" */
987 static gboolean
glade_command_set_name_undo(GladeCommand * cmd)988 glade_command_set_name_undo (GladeCommand *cmd)
989 {
990   return glade_command_set_name_execute (cmd);
991 }
992 
993 /*
994  * Execute the set name command and revert it.  Ie, after the execution of this
995  * function cmd will point to the undo action
996  */
997 static gboolean
glade_command_set_name_execute(GladeCommand * cmd)998 glade_command_set_name_execute (GladeCommand *cmd)
999 {
1000   GladeCommandSetName *me = GLADE_COMMAND_SET_NAME (cmd);
1001   char *tmp;
1002 
1003   g_return_val_if_fail (me != NULL, TRUE);
1004   g_return_val_if_fail (me->widget != NULL, TRUE);
1005   g_return_val_if_fail (me->name != NULL, TRUE);
1006 
1007   glade_project_set_widget_name (cmd->priv->project, me->widget, me->name);
1008 
1009   tmp = me->old_name;
1010   me->old_name = me->name;
1011   me->name = tmp;
1012 
1013   return TRUE;
1014 }
1015 
1016 static void
glade_command_set_name_finalize(GObject * obj)1017 glade_command_set_name_finalize (GObject *obj)
1018 {
1019   GladeCommandSetName *me;
1020 
1021   g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (obj));
1022 
1023   me = GLADE_COMMAND_SET_NAME (obj);
1024 
1025   g_free (me->old_name);
1026   g_free (me->name);
1027 
1028   glade_command_finalize (obj);
1029 }
1030 
1031 static gboolean
glade_command_set_name_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)1032 glade_command_set_name_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
1033 {
1034   GladeCommandSetName *cmd1;
1035   GladeCommandSetName *cmd2;
1036 
1037   if (!other_cmd)
1038     {
1039       if (GLADE_IS_COMMAND_SET_NAME (this_cmd))
1040         {
1041           cmd1 = (GladeCommandSetName *) this_cmd;
1042 
1043 	  return (g_strcmp0 (cmd1->old_name, cmd1->name) == 0);
1044         }
1045       return FALSE;
1046     }
1047 
1048   if (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
1049       GLADE_IS_COMMAND_SET_NAME (other_cmd))
1050     {
1051       cmd1 = GLADE_COMMAND_SET_NAME (this_cmd);
1052       cmd2 = GLADE_COMMAND_SET_NAME (other_cmd);
1053 
1054       return (cmd1->widget == cmd2->widget);
1055     }
1056 
1057   return FALSE;
1058 }
1059 
1060 static void
glade_command_set_name_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)1061 glade_command_set_name_collapse (GladeCommand *this_cmd,
1062                                  GladeCommand *other_cmd)
1063 {
1064   GladeCommandSetName *nthis = GLADE_COMMAND_SET_NAME (this_cmd);
1065   GladeCommandSetName *nother = GLADE_COMMAND_SET_NAME (other_cmd);
1066 
1067   g_return_if_fail (GLADE_IS_COMMAND_SET_NAME (this_cmd) &&
1068                     GLADE_IS_COMMAND_SET_NAME (other_cmd));
1069 
1070   g_free (nthis->old_name);
1071   nthis->old_name = nother->old_name;
1072   nother->old_name = NULL;
1073 
1074   g_free (this_cmd->priv->description);
1075   this_cmd->priv->description =
1076       g_strdup_printf (_("Renaming %s to %s"), nthis->name, nthis->old_name);
1077 }
1078 
1079 /* this function takes the ownership of name */
1080 void
glade_command_set_name(GladeWidget * widget,const gchar * name)1081 glade_command_set_name (GladeWidget *widget, const gchar *name)
1082 {
1083   GladeCommandSetName *me;
1084   GladeCommand *cmd;
1085 
1086   g_return_if_fail (GLADE_IS_WIDGET (widget));
1087   g_return_if_fail (name && name[0]);
1088 
1089   /* Dont spam the queue with false name changes.
1090    */
1091   if (!strcmp (glade_widget_get_name (widget), name))
1092     return;
1093 
1094   me = g_object_new (GLADE_COMMAND_SET_NAME_TYPE, NULL);
1095   cmd = GLADE_COMMAND (me);
1096   cmd->priv->project = glade_widget_get_project (widget);
1097 
1098   me->widget = widget;
1099   me->name = g_strdup (name);
1100   me->old_name = g_strdup (glade_widget_get_name (widget));
1101 
1102   cmd->priv->description =
1103       g_strdup_printf (_("Renaming %s to %s"), me->old_name, me->name);
1104 
1105   glade_command_check_group (GLADE_COMMAND (me));
1106 
1107   if (glade_command_set_name_execute (GLADE_COMMAND (me)))
1108     glade_project_push_undo (cmd->priv->project, cmd);
1109   else
1110     g_object_unref (G_OBJECT (me));
1111 }
1112 
1113 /******************************************************************************
1114  *
1115  * add/remove
1116  *
1117  * These canonical commands add/remove a widget list to/from the project.
1118  *
1119  *****************************************************************************/
1120 
1121 typedef struct
1122 {
1123   GladeCommand parent;
1124   GList *widgets;
1125   gboolean add;
1126   gboolean from_clipboard;
1127 } GladeCommandAddRemove;
1128 
1129 
1130 GLADE_MAKE_COMMAND (GladeCommandAddRemove, glade_command_add_remove);
1131 #define GLADE_COMMAND_ADD_REMOVE_TYPE			(glade_command_add_remove_get_type ())
1132 #define GLADE_COMMAND_ADD_REMOVE(o)	  			(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_ADD_REMOVE_TYPE, GladeCommandAddRemove))
1133 #define GLADE_COMMAND_ADD_REMOVE_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_ADD_REMOVE_TYPE, GladeCommandAddRemoveClass))
1134 #define GLADE_IS_COMMAND_ADD_REMOVE(o)			(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_ADD_REMOVE_TYPE))
1135 #define GLADE_IS_COMMAND_ADD_REMOVE_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_ADD_REMOVE_TYPE))
1136 
1137 static void
glade_command_placeholder_destroyed(GtkWidget * object,CommandData * cdata)1138 glade_command_placeholder_destroyed (GtkWidget *object, CommandData *cdata)
1139 {
1140   if (GTK_WIDGET (cdata->placeholder) == object)
1141     {
1142       cdata->placeholder = NULL;
1143       cdata->handler_id = 0;
1144     }
1145 }
1146 
1147 static void
glade_command_placeholder_connect(CommandData * cdata,GladePlaceholder * placeholder)1148 glade_command_placeholder_connect (CommandData *cdata,
1149                                    GladePlaceholder *placeholder)
1150 {
1151   g_assert (cdata && cdata->placeholder == NULL);
1152 
1153   /* Something like a no-op with no placeholder */
1154   if ((cdata->placeholder = placeholder) == NULL)
1155     return;
1156 
1157   cdata->handler_id = g_signal_connect
1158       (placeholder, "destroy",
1159        G_CALLBACK (glade_command_placeholder_destroyed), cdata);
1160 }
1161 
1162 /**
1163  * get_all_parentless_reffed_widgetst:
1164  *
1165  * @props (element-type GladeWidget) : List of #GladeWidget
1166  * @return (element-type GladeWidget) : List of #GladeWidget
1167  */
1168 static GList *
get_all_parentless_reffed_widgets(GList * reffed,GladeWidget * widget)1169 get_all_parentless_reffed_widgets (GList *reffed, GladeWidget *widget)
1170 {
1171   GList *children, *l, *list;
1172   GladeWidget *child;
1173 
1174   if ((list = glade_widget_get_parentless_reffed_widgets (widget)) != NULL)
1175     reffed = g_list_concat (reffed, list);
1176 
1177   children = glade_widget_get_children (widget);
1178 
1179   for (l = children; l; l = l->next)
1180     {
1181       child  = glade_widget_get_from_gobject (l->data);
1182       reffed = get_all_parentless_reffed_widgets (reffed, child);
1183     }
1184 
1185   g_list_free (children);
1186 
1187   return reffed;
1188 }
1189 
1190 /**
1191  * glade_command_add:
1192  * @widgets (element-type GladeWidget): a #Glist
1193  * @parent: a #GladeWidget
1194  * @placeholder: a #GladePlaceholder
1195  * @pasting: whether we are pasting an existing widget or creating a new one.
1196  *
1197  * Performs an add command on all widgets in @widgets to @parent, possibly
1198  * replacing @placeholder (note toplevels dont need a parent; the active project
1199  * will be used when pasting toplevel objects).
1200  * Pasted widgets will persist packing properties from thier cut/copy source
1201  * while newly added widgets will prefer packing defaults.
1202  *
1203  */
1204 void
glade_command_add(GList * widgets,GladeWidget * parent,GladePlaceholder * placeholder,GladeProject * project,gboolean pasting)1205 glade_command_add (GList            *widgets,
1206                    GladeWidget      *parent,
1207                    GladePlaceholder *placeholder,
1208 		   GladeProject     *project,
1209 		   gboolean          pasting)
1210 {
1211   GladeCommandAddRemove *me;
1212   GladeCommand *cmd;
1213   CommandData *cdata;
1214   GladeWidget *widget = NULL;
1215   GladeWidgetAdaptor *adaptor;
1216   GList *l, *list, *children, *placeholders = NULL;
1217   GtkWidget *child;
1218 
1219   g_return_if_fail (widgets && widgets->data);
1220   g_return_if_fail (parent == NULL || GLADE_IS_WIDGET (parent));
1221 
1222   me = g_object_new (GLADE_COMMAND_ADD_REMOVE_TYPE, NULL);
1223   cmd = GLADE_COMMAND (me);
1224   me->add = TRUE;
1225   me->from_clipboard = pasting;
1226 
1227   /* Things can go wrong in this function if the dataset is inacurate,
1228    * we make no real attempt here to recover, just g_critical() and
1229    * fix the bugs as they pop up.
1230    */
1231   widget  = GLADE_WIDGET (widgets->data);
1232   adaptor = glade_widget_get_adaptor (widget);
1233 
1234   if (placeholder && GWA_IS_TOPLEVEL (adaptor) == FALSE)
1235     cmd->priv->project = glade_placeholder_get_project (placeholder);
1236   else
1237     cmd->priv->project = project;
1238 
1239   GLADE_COMMAND (me)->priv->description =
1240       g_strdup_printf (_("Add %s"), g_list_length (widgets) == 1 ?
1241                        glade_widget_get_name (widget) : _("multiple"));
1242 
1243   for (list = widgets; list && list->data; list = list->next)
1244     {
1245       widget = list->data;
1246       cdata = g_new0 (CommandData, 1);
1247       if (glade_widget_get_internal (widget))
1248         g_critical ("Internal widget in Add");
1249 
1250       adaptor = glade_widget_get_adaptor (widget);
1251 
1252       /* Widget */
1253       cdata->widget = g_object_ref (G_OBJECT (widget));
1254 
1255       /* Parentless ref */
1256       if ((cdata->reffed =
1257            get_all_parentless_reffed_widgets (cdata->reffed, widget)) != NULL)
1258         g_list_foreach (cdata->reffed, (GFunc) g_object_ref, NULL);
1259 
1260       /* Parent */
1261       if (parent == NULL)
1262 	cdata->parent = glade_widget_get_parent (widget);
1263       else if (placeholder && GWA_IS_TOPLEVEL (adaptor) == FALSE)
1264 	cdata->parent = glade_placeholder_get_parent (placeholder);
1265       else
1266 	cdata->parent = parent;
1267 
1268       /* Placeholder */
1269       if (placeholder != NULL && g_list_length (widgets) == 1)
1270         {
1271           glade_command_placeholder_connect (cdata, placeholder);
1272         }
1273       else if (cdata->parent &&
1274                glade_widget_placeholder_relation (cdata->parent, widget))
1275         {
1276           if ((children =
1277                glade_widget_adaptor_get_children (glade_widget_get_adaptor (cdata->parent),
1278                                                   glade_widget_get_object (cdata->parent))) != NULL)
1279             {
1280               for (l = children; l && l->data; l = l->next)
1281                 {
1282                   child = l->data;
1283 
1284                   /* Find a placeholder for this child */
1285                   if (GLADE_IS_PLACEHOLDER (child) &&
1286                       g_list_find (placeholders, child) == NULL)
1287                     {
1288                       placeholders = g_list_append (placeholders, child);
1289                       glade_command_placeholder_connect (cdata, GLADE_PLACEHOLDER (child));
1290                       break;
1291                     }
1292                 }
1293               g_list_free (children);
1294             }
1295         }
1296 
1297       me->widgets = g_list_prepend (me->widgets, cdata);
1298     }
1299 
1300   glade_command_check_group (GLADE_COMMAND (me));
1301 
1302   /*
1303    * Push it onto the undo stack only on success
1304    */
1305   if (glade_command_add_remove_execute (cmd))
1306     glade_project_push_undo (cmd->priv->project, cmd);
1307   else
1308     g_object_unref (G_OBJECT (me));
1309 
1310   if (placeholders)
1311     g_list_free (placeholders);
1312 
1313 }
1314 
1315 static void
glade_command_delete_prop_refs(GladeWidget * widget)1316 glade_command_delete_prop_refs (GladeWidget *widget)
1317 {
1318   GladeProperty *property;
1319   GList         *refs, *l;
1320 
1321   refs = glade_widget_list_prop_refs (widget);
1322   for (l = refs; l; l = l->next)
1323     {
1324       property = l->data;
1325       glade_command_set_property (property, NULL);
1326     }
1327   g_list_free (refs);
1328 }
1329 
1330 static void glade_command_remove (GList *widgets);
1331 
1332 static void
glade_command_remove_locked(GladeWidget * widget,GList * reffed)1333 glade_command_remove_locked (GladeWidget *widget, GList *reffed)
1334 {
1335   GList list = { 0, }, *widgets, *l;
1336   GladeWidget *locked;
1337 
1338   widgets = glade_widget_list_locked_widgets (widget);
1339 
1340   for (l = widgets; l; l = l->next)
1341     {
1342       locked = l->data;
1343       list.data = locked;
1344 
1345       if (g_list_find (reffed, locked))
1346         continue;
1347 
1348       glade_command_unlock_widget (locked);
1349       glade_command_remove (&list);
1350     }
1351 
1352   g_list_free (widgets);
1353 }
1354 
1355 /**
1356  * glade_command_remove:
1357  * @widgets (element-type GladeWidget): a #GList of #GladeWidgets
1358  * @return_placeholders: whether or not to return a list of placehodlers
1359  *
1360  * Performs a remove command on all widgets in @widgets from @parent.
1361  */
1362 static void
glade_command_remove(GList * widgets)1363 glade_command_remove (GList *widgets)
1364 {
1365   GladeCommandAddRemove *me;
1366   GladeWidget *widget = NULL;
1367   GladeWidget *lock;
1368   CommandData *cdata;
1369   GtkWidget *placeholder;
1370   GList *list, *l;
1371 
1372   g_return_if_fail (widgets != NULL);
1373 
1374   /* internal children cannot be deleted. Notify the user. */
1375   for (list = widgets; list && list->data; list = list->next)
1376     {
1377       widget = list->data;
1378       lock   = glade_widget_get_locker (widget);
1379 
1380       if (glade_widget_get_internal (widget))
1381         {
1382           glade_util_ui_message (glade_app_get_window (),
1383                                  GLADE_UI_WARN, NULL,
1384                                  _
1385                                  ("You cannot remove a widget internal to a composite widget."));
1386           return;
1387         }
1388       else if (lock)
1389         {
1390           glade_util_ui_message (glade_app_get_window (),
1391                                  GLADE_UI_WARN, NULL,
1392                                  _("%s is locked by %s, edit %s first."),
1393                                  glade_widget_get_name (widget),
1394 				 glade_widget_get_name (lock),
1395                                  glade_widget_get_name (lock));
1396           return;
1397         }
1398     }
1399 
1400   me = g_object_new (GLADE_COMMAND_ADD_REMOVE_TYPE, NULL);
1401   me->add = FALSE;
1402   me->from_clipboard = FALSE;
1403 
1404   GLADE_COMMAND (me)->priv->project = glade_widget_get_project (widget);
1405   GLADE_COMMAND (me)->priv->description = g_strdup ("dummy");
1406 
1407   if (g_list_length (widgets) == 1)
1408     glade_command_push_group (_("Remove %s"),
1409                               glade_widget_get_name (GLADE_WIDGET (widgets->data)));
1410   else
1411     glade_command_push_group (_("Remove multiple"));
1412 
1413   for (list = widgets; list && list->data; list = list->next)
1414     {
1415       widget = list->data;
1416 
1417       cdata = g_new0 (CommandData, 1);
1418       cdata->widget = g_object_ref (G_OBJECT (widget));
1419       cdata->parent = glade_widget_get_parent (widget);
1420 
1421       if ((cdata->reffed =
1422            get_all_parentless_reffed_widgets (cdata->reffed, widget)) != NULL)
1423         g_list_foreach (cdata->reffed, (GFunc) g_object_ref, NULL);
1424 
1425       /* If we're removing the template widget, then we need to unset it as template */
1426       if (glade_project_get_template (GLADE_COMMAND (me)->priv->project) == widget)
1427 	glade_command_set_project_template (GLADE_COMMAND (me)->priv->project, NULL);
1428 
1429       /* Undoably unset any object properties that may point to the removed object */
1430       glade_command_delete_prop_refs (widget);
1431 
1432       /* Undoably unlock and remove any widgets locked by this widget */
1433       glade_command_remove_locked (widget, cdata->reffed);
1434 
1435       if (cdata->parent != NULL &&
1436           glade_widget_placeholder_relation (cdata->parent, cdata->widget))
1437         {
1438           placeholder = glade_placeholder_new ();
1439           glade_command_placeholder_connect
1440               (cdata, GLADE_PLACEHOLDER (placeholder));
1441         }
1442       me->widgets = g_list_prepend (me->widgets, cdata);
1443 
1444       /* Record packing props if not deleted from the clipboard */
1445       if (me->from_clipboard == FALSE)
1446 	{
1447 	  for (l = glade_widget_get_packing_properties (widget); l; l = l->next)
1448 	    cdata->pack_props =
1449 	      g_list_prepend (cdata->pack_props,
1450 			      glade_property_dup (GLADE_PROPERTY (l->data),
1451 						  cdata->widget));
1452 	}
1453     }
1454 
1455   g_assert (widget);
1456 
1457   glade_command_check_group (GLADE_COMMAND (me));
1458 
1459   if (glade_command_add_remove_execute (GLADE_COMMAND (me)))
1460     glade_project_push_undo (GLADE_COMMAND (me)->priv->project,
1461                              GLADE_COMMAND (me));
1462   else
1463     g_object_unref (G_OBJECT (me));
1464 
1465   glade_command_pop_group ();
1466 }                               /* end of glade_command_remove() */
1467 
1468 static void
glade_command_transfer_props(GladeWidget * gnew,GList * saved_props)1469 glade_command_transfer_props (GladeWidget *gnew, GList *saved_props)
1470 {
1471   GList *l;
1472 
1473   for (l = saved_props; l; l = l->next)
1474     {
1475       GladeProperty *prop, *sprop = l->data;
1476       GladePropertyClass *pclass = glade_property_get_class (sprop);
1477 
1478       prop = glade_widget_get_pack_property (gnew, glade_property_class_id (pclass));
1479 
1480       if (prop && glade_property_class_transfer_on_paste (pclass) &&
1481           glade_property_class_match (glade_property_get_class (prop), pclass))
1482         glade_property_set_value (prop, glade_property_inline_value (sprop));
1483     }
1484 }
1485 
1486 static gboolean
glade_command_add_execute(GladeCommandAddRemove * me)1487 glade_command_add_execute (GladeCommandAddRemove *me)
1488 {
1489   CommandData *cdata;
1490   GList *list, *l, *saved_props;
1491   gchar *special_child_type;
1492 
1493   if (me->widgets)
1494     {
1495       glade_project_selection_clear (GLADE_COMMAND (me)->priv->project, FALSE);
1496 
1497       for (list = me->widgets; list && list->data; list = list->next)
1498         {
1499           cdata = list->data;
1500           saved_props = NULL;
1501 
1502 	  GLADE_NOTE (COMMANDS,
1503 		      g_print ("Adding widget '%s' to parent '%s' "
1504 			       "(from clipboard: %s, props recorded: %s, have placeholder: %s, child_type: %s)\n",
1505 			       glade_widget_get_name (cdata->widget),
1506 			       cdata->parent ? glade_widget_get_name (cdata->parent) : "(none)",
1507 			       me->from_clipboard ? "yes" : "no",
1508 			       cdata->props_recorded ? "yes" : "no",
1509 			       cdata->placeholder ? "yes" : "no",
1510 			       cdata->special_type));
1511 
1512           if (cdata->parent != NULL)
1513             {
1514               /* Prepare special-child-type for the paste. */
1515               if (me->from_clipboard)
1516                 {
1517                   /* Only transfer properties when they are from the clipboard,
1518                    * otherwise prioritize packing defaults.
1519                    */
1520                   saved_props =
1521                       glade_widget_dup_properties (cdata->widget,
1522                                                    glade_widget_get_packing_properties (cdata->widget),
1523 						   FALSE, FALSE, FALSE);
1524 
1525                   glade_widget_set_packing_properties (cdata->widget, cdata->parent);
1526                 }
1527 
1528 	      /* Clear it the first time around, ensure we record it after adding */
1529 	      if (cdata->props_recorded == FALSE)
1530 		g_object_set_data (glade_widget_get_object (cdata->widget),
1531 				   "special-child-type", NULL);
1532 	      else
1533 		g_object_set_data_full (glade_widget_get_object (cdata->widget),
1534 					"special-child-type",
1535 					g_strdup (cdata->special_type),
1536 					g_free);
1537 
1538               /* glade_command_paste ganauntees that if
1539                * there we are pasting to a placeholder,
1540                * there is only one widget.
1541                */
1542               if (cdata->placeholder)
1543 		glade_widget_replace (cdata->parent,
1544 				      G_OBJECT (cdata->placeholder),
1545 				      glade_widget_get_object (cdata->widget));
1546               else
1547 		glade_widget_add_child (cdata->parent,
1548 					cdata->widget,
1549 					cdata->props_recorded == FALSE);
1550 
1551               glade_command_transfer_props (cdata->widget, saved_props);
1552 
1553               if (saved_props)
1554                 {
1555                   g_list_foreach (saved_props, (GFunc) g_object_unref, NULL);
1556                   g_list_free (saved_props);
1557                 }
1558 
1559               /* Now that we've added, apply any packing props if nescisary. */
1560               for (l = cdata->pack_props; l; l = l->next)
1561                 {
1562                   GValue              value = { 0, };
1563                   GladeProperty      *saved_prop = l->data;
1564 		  GladePropertyClass *pclass = glade_property_get_class (saved_prop);
1565                   GladeProperty      *widget_prop =
1566 		    glade_widget_get_pack_property (cdata->widget, glade_property_class_id (pclass));
1567 
1568                   glade_property_get_value (saved_prop, &value);
1569                   glade_property_set_value (widget_prop, &value);
1570 		  glade_property_sync (widget_prop);
1571                   g_value_unset (&value);
1572                 }
1573 
1574               if (cdata->props_recorded == FALSE)
1575                 {
1576 
1577                   /* Save the packing properties after the initial paste.
1578                    * (this will be the defaults returned by the container
1579                    * implementation after initially adding them).
1580                    *
1581                    * Otherwise this recorded marker was set when cutting
1582                    */
1583                   g_assert (cdata->pack_props == NULL);
1584                   for (l = glade_widget_get_packing_properties (cdata->widget); l; l = l->next)
1585                     cdata->pack_props =
1586 		      g_list_prepend (cdata->pack_props,
1587 				      glade_property_dup (GLADE_PROPERTY (l->data),
1588 							  cdata->widget));
1589 
1590                   /* Record the special-type here after replacing */
1591                   if ((special_child_type =
1592                        g_object_get_data (glade_widget_get_object (cdata->widget),
1593                                           "special-child-type")) != NULL)
1594                     {
1595                       g_free (cdata->special_type);
1596                       cdata->special_type = g_strdup (special_child_type);
1597                     }
1598 
1599 		  GLADE_NOTE (COMMANDS,
1600 			      g_print ("Recorded properties for adding widget '%s' to parent '%s' (special child: %s)\n",
1601 				       glade_widget_get_name (cdata->widget),
1602 				       cdata->parent ? glade_widget_get_name (cdata->parent) : "(none)",
1603 				       cdata->special_type));
1604 
1605                   /* Mark the properties as recorded */
1606                   cdata->props_recorded = TRUE;
1607                 }
1608             }
1609 
1610           glade_project_add_object (GLADE_COMMAND (me)->priv->project,
1611 				    glade_widget_get_object (cdata->widget));
1612 
1613           for (l = cdata->reffed; l; l = l->next)
1614             {
1615               GladeWidget *reffed = l->data;
1616               glade_project_add_object (GLADE_COMMAND (me)->priv->project,
1617                                         glade_widget_get_object (reffed));
1618             }
1619 
1620 	  glade_project_selection_add (GLADE_COMMAND (me)->priv->project,
1621 				       glade_widget_get_object (cdata->widget), FALSE);
1622 
1623           glade_widget_show (cdata->widget);
1624         }
1625 
1626       glade_project_queue_selection_changed (GLADE_COMMAND (me)->priv->project);
1627     }
1628   return TRUE;
1629 }                               /* end of glade_command_add_execute() */
1630 
1631 static gboolean
glade_command_remove_execute(GladeCommandAddRemove * me)1632 glade_command_remove_execute (GladeCommandAddRemove *me)
1633 {
1634   CommandData *cdata;
1635   GladeWidget *reffed;
1636   GList *list, *l;
1637   gchar *special_child_type;
1638 
1639   for (list = me->widgets; list && list->data; list = list->next)
1640     {
1641       cdata = list->data;
1642 
1643       GLADE_NOTE (COMMANDS,
1644 		  g_print ("Removing widget '%s' from parent '%s' "
1645 			   "(from clipboard: %s, props recorded: %s, have placeholder: %s, child_type: %s)\n",
1646 			   glade_widget_get_name (cdata->widget),
1647 			   cdata->parent ? glade_widget_get_name (cdata->parent) : "(none)",
1648 			   me->from_clipboard ? "yes" : "no",
1649 			   cdata->props_recorded ? "yes" : "no",
1650 			   cdata->placeholder ? "yes" : "no",
1651 			   cdata->special_type));
1652 
1653       glade_widget_hide (cdata->widget);
1654 
1655       if (cdata->props_recorded == FALSE)
1656 	{
1657 	  /* Record the special-type here after replacing */
1658 	  if ((special_child_type =
1659 	       g_object_get_data (glade_widget_get_object (cdata->widget),
1660 				  "special-child-type")) != NULL)
1661 	    {
1662 	      g_free (cdata->special_type);
1663 	      cdata->special_type = g_strdup (special_child_type);
1664 	    }
1665 
1666 	  GLADE_NOTE (COMMANDS,
1667 		      g_print ("Recorded properties for removing widget '%s' from parent '%s' (special child: %s)\n",
1668 			       glade_widget_get_name (cdata->widget),
1669 			       cdata->parent ? glade_widget_get_name (cdata->parent) : "(none)",
1670 			       cdata->special_type));
1671 
1672 	  /* Mark the properties as recorded */
1673 	  cdata->props_recorded = TRUE;
1674 	}
1675 
1676       glade_project_remove_object (GLADE_COMMAND (me)->priv->project,
1677                                    glade_widget_get_object (cdata->widget));
1678 
1679       for (l = cdata->reffed; l; l = l->next)
1680         {
1681           reffed = l->data;
1682           glade_project_remove_object (GLADE_COMMAND (me)->priv->project,
1683                                        glade_widget_get_object (reffed));
1684         }
1685 
1686       if (cdata->parent)
1687         {
1688           if (cdata->placeholder)
1689             glade_widget_replace (cdata->parent, glade_widget_get_object (cdata->widget),
1690                                   G_OBJECT (cdata->placeholder));
1691           else
1692             glade_widget_remove_child (cdata->parent, cdata->widget);
1693         }
1694     }
1695 
1696   return TRUE;
1697 }
1698 
1699 /*
1700  * Execute the cmd and revert it.  Ie, after the execution of this
1701  * function cmd will point to the undo action
1702  */
1703 static gboolean
glade_command_add_remove_execute(GladeCommand * cmd)1704 glade_command_add_remove_execute (GladeCommand *cmd)
1705 {
1706   GladeCommandAddRemove *me = (GladeCommandAddRemove *) cmd;
1707   gboolean retval;
1708 
1709   if (me->add)
1710     retval = glade_command_add_execute (me);
1711   else
1712     retval = glade_command_remove_execute (me);
1713 
1714   me->add = !me->add;
1715 
1716   return retval;
1717 }
1718 
1719 static gboolean
glade_command_add_remove_undo(GladeCommand * cmd)1720 glade_command_add_remove_undo (GladeCommand *cmd)
1721 {
1722   return glade_command_add_remove_execute (cmd);
1723 }
1724 
1725 static void
glade_command_add_remove_finalize(GObject * obj)1726 glade_command_add_remove_finalize (GObject *obj)
1727 {
1728   GladeCommandAddRemove *cmd;
1729   CommandData *cdata;
1730   GList *list;
1731 
1732   g_return_if_fail (GLADE_IS_COMMAND_ADD_REMOVE (obj));
1733 
1734   cmd = GLADE_COMMAND_ADD_REMOVE (obj);
1735 
1736   for (list = cmd->widgets; list && list->data; list = list->next)
1737     {
1738       cdata = list->data;
1739 
1740       if (cdata->placeholder)
1741         {
1742           if (cdata->handler_id)
1743             g_signal_handler_disconnect (cdata->placeholder, cdata->handler_id);
1744           if (g_object_is_floating (G_OBJECT (cdata->placeholder)))
1745             gtk_widget_destroy (GTK_WIDGET (cdata->placeholder));
1746         }
1747 
1748       if (cdata->widget)
1749         g_object_unref (G_OBJECT (cdata->widget));
1750 
1751       g_list_foreach (cdata->reffed, (GFunc) g_object_unref, NULL);
1752       g_list_free (cdata->reffed);
1753     }
1754   g_list_free (cmd->widgets);
1755 
1756   glade_command_finalize (obj);
1757 }
1758 
1759 static gboolean
glade_command_add_remove_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)1760 glade_command_add_remove_unifies (GladeCommand *this_cmd,
1761                                   GladeCommand *other_cmd)
1762 {
1763   return FALSE;
1764 }
1765 
1766 static void
glade_command_add_remove_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)1767 glade_command_add_remove_collapse (GladeCommand *this_cmd,
1768                                    GladeCommand *other_cmd)
1769 {
1770   g_return_if_reached ();
1771 }
1772 
1773 /******************************************************************************
1774  *
1775  * The following are command aliases.  Their implementations are the actual
1776  * glade commands.
1777  *
1778  *****************************************************************************/
1779 
1780 /**
1781  * glade_command_create:
1782  * @adaptor:		A #GladeWidgetAdaptor
1783  * @parent (allow-none):             the parent #GladeWidget to add the new widget to.
1784  * @placeholder (allow-none):	the placeholder which will be substituted by the widget
1785  * @project:            the project his widget belongs to.
1786  *
1787  * Creates a new widget using @adaptor and put in place of the @placeholder
1788  * in the @project
1789  *
1790  * Returns: the newly created widget.
1791  */
1792 GladeWidget *
glade_command_create(GladeWidgetAdaptor * adaptor,GladeWidget * parent,GladePlaceholder * placeholder,GladeProject * project)1793 glade_command_create (GladeWidgetAdaptor *adaptor,
1794                       GladeWidget *parent,
1795                       GladePlaceholder *placeholder,
1796                       GladeProject *project)
1797 {
1798   GladeWidget *widget;
1799   GList *widgets = NULL;
1800 
1801   g_return_val_if_fail (GLADE_IS_WIDGET_ADAPTOR (adaptor), NULL);
1802   g_return_val_if_fail (GLADE_IS_PROJECT (project), NULL);
1803 
1804   /* attempt to create the widget -- widget may be null, e.g. the user clicked cancel on a query */
1805   widget = glade_widget_adaptor_create_widget (adaptor, TRUE,
1806                                                "parent", parent,
1807                                                "project", project, NULL);
1808   if (widget == NULL)
1809     {
1810       return NULL;
1811     }
1812 
1813   if (parent && !glade_widget_add_verify (parent, widget, TRUE))
1814     {
1815       g_object_ref_sink (widget);
1816       g_object_unref (widget);
1817       return NULL;
1818     }
1819 
1820   widgets = g_list_prepend (widgets, widget);
1821   glade_command_push_group (_("Create %s"), glade_widget_get_name (widget));
1822   glade_command_add (widgets, parent, placeholder, project, FALSE);
1823   glade_command_pop_group ();
1824 
1825   g_list_free (widgets);
1826 
1827   /* Make selection change immediately when a widget is created */
1828   glade_project_selection_changed (project);
1829 
1830   return widget;
1831 }
1832 
1833 /**
1834  * glade_command_delete:
1835  * @widgets (element-type GladeWidget): a #GList of #GladeWidgets
1836  *
1837  * Performs a delete command on the list of widgets.
1838  */
1839 void
glade_command_delete(GList * widgets)1840 glade_command_delete (GList *widgets)
1841 {
1842   GladeWidget *widget;
1843 
1844   g_return_if_fail (widgets != NULL);
1845 
1846   widget = widgets->data;
1847   glade_command_push_group (_("Delete %s"),
1848                             g_list_length (widgets) == 1 ?
1849 			    glade_widget_get_name (widget) : _("multiple"));
1850   glade_command_remove (widgets);
1851   glade_command_pop_group ();
1852 }
1853 
1854 /**
1855  * glade_command_cut:
1856  * @widgets (element-type GladeWidget): a #GList of #GladeWidgets
1857  *
1858  * Removes the list of widgets and adds them to the clipboard.
1859  */
1860 void
glade_command_cut(GList * widgets)1861 glade_command_cut (GList *widgets)
1862 {
1863   GladeWidget *widget;
1864   GList *l;
1865 
1866   g_return_if_fail (widgets != NULL);
1867 
1868   for (l = widgets; l; l = l->next)
1869     g_object_set_data (G_OBJECT (l->data), "glade-command-was-cut",
1870                        GINT_TO_POINTER (TRUE));
1871 
1872   widget = widgets->data;
1873   glade_command_push_group (_("Cut %s"),
1874                             g_list_length (widgets) == 1 ?
1875 			    glade_widget_get_name (widget) : _("multiple"));
1876   glade_command_remove (widgets);
1877   glade_command_pop_group ();
1878 
1879   glade_clipboard_add (glade_app_get_clipboard (), widgets);
1880 }
1881 
1882 #if 0
1883 static void
1884 glade_command_break_references_for_widget (GladeWidget *widget, GList *widgets)
1885 {
1886   GList *l, *children;
1887 
1888   for (l = widget->properties; l; l = l->next)
1889     {
1890       property = l->data;
1891 
1892       if (glade_property_class_is_object (property->klass) &&
1893           property->klass->parentless_widget == FALSE)
1894         {
1895           GList *obj_list;
1896           GObject *reffed_obj = NULL;
1897           GladeWidget *reffed_widget;
1898 
1899           if (GLADE_IS_PARAM_SPEC_OBJECTS (klass->pspec))
1900             {
1901               glade_property_get (property, &obj_list);
1902 
1903             }
1904           else
1905             {
1906               glade_property_get (property, &reffed_obj);
1907             }
1908         }
1909     }
1910 
1911   children = glade_widget_adaptor_get_children (widget->adaptor,
1912                                                 widget->object);
1913 
1914   for (l = children; l; l = l->next)
1915     {
1916       if ((child = glade_widget_get_from_gobject (l->data)) != NULL)
1917         glade_command_break_references_for_widget (child, widgets);
1918     }
1919 
1920   g_list_free (children);
1921 }
1922 
1923 static void
1924 glade_command_break_references (GladeProject *project, GList *widgets)
1925 {
1926   GList *list;
1927   GladeWidget *widget;
1928 
1929   for (list = widgets; list && list->data; list = list->next)
1930     {
1931       widget = l->data;
1932 
1933       if (project == widget->project)
1934         continue;
1935 
1936       glade_command_break_references_for_widget (widget, widgets);
1937     }
1938 
1939 
1940 }
1941 #endif
1942 
1943 /**
1944  * glade_command_paste:
1945  * @widgets (element-type GladeWidget): a #GList of #GladeWidget
1946  * @parent (allow-none): a #GladeWidget
1947  * @placeholder (allow-none): a #GladePlaceholder
1948  *
1949  * Performs a paste command on all widgets in @widgets to @parent, possibly
1950  * replacing @placeholder (note toplevels dont need a parent; the active project
1951  * will be used when pasting toplevel objects).
1952  */
1953 void
glade_command_paste(GList * widgets,GladeWidget * parent,GladePlaceholder * placeholder,GladeProject * project)1954 glade_command_paste (GList *widgets,
1955                      GladeWidget *parent,
1956                      GladePlaceholder *placeholder,
1957 		     GladeProject *project)
1958 {
1959   GList *list, *copied_widgets = NULL;
1960   GladeWidget *copied_widget = NULL;
1961   gboolean exact;
1962 
1963   g_return_if_fail (widgets != NULL);
1964 
1965   for (list = widgets; list && list->data; list = list->next)
1966     {
1967       exact =
1968           GPOINTER_TO_INT (g_object_get_data
1969                            (G_OBJECT (list->data), "glade-command-was-cut"));
1970 
1971       copied_widget = glade_widget_dup (list->data, exact);
1972       copied_widgets = g_list_prepend (copied_widgets, copied_widget);
1973     }
1974 
1975   glade_command_push_group (_("Paste %s"),
1976                             g_list_length (widgets) == 1 ?
1977 			    glade_widget_get_name (copied_widget) : _("multiple"));
1978 
1979   glade_command_add (copied_widgets, parent, placeholder, project, TRUE);
1980   glade_command_pop_group ();
1981 
1982   if (copied_widgets)
1983     g_list_free (copied_widgets);
1984 }
1985 
1986 /**
1987  * glade_command_dnd:
1988  * @widgets (element-type GladeWidget): a #GList of #GladeWidget
1989  * @parent (allow-none): a #GladeWidget
1990  * @placeholder (allow-none): a #GladePlaceholder
1991  *
1992  * Performs a drag-n-drop command, i.e. removes the list of widgets and adds them
1993  * to the new parent, possibly replacing @placeholder (note toplevels dont need a
1994  * parent; the active project will be used when pasting toplevel objects).
1995  */
1996 void
glade_command_dnd(GList * widgets,GladeWidget * parent,GladePlaceholder * placeholder)1997 glade_command_dnd (GList *widgets,
1998                    GladeWidget *parent,
1999                    GladePlaceholder *placeholder)
2000 {
2001   GladeWidget *widget;
2002   GladeProject *project;
2003 
2004   g_return_if_fail (widgets != NULL);
2005 
2006   widget = widgets->data;
2007 
2008   if (parent)
2009     project = glade_widget_get_project (parent);
2010   else if (placeholder)
2011     project = glade_placeholder_get_project (placeholder);
2012   else
2013     project = glade_widget_get_project (widget);
2014 
2015   g_return_if_fail (project);
2016 
2017   glade_command_push_group (_("Drag %s and Drop to %s"),
2018                             g_list_length (widgets) == 1 ?
2019 			    glade_widget_get_name (widget) : _("multiple"),
2020                             parent ? glade_widget_get_name (parent) : _("root"));
2021   glade_command_remove (widgets);
2022   glade_command_add (widgets, parent, placeholder, project, TRUE);
2023   glade_command_pop_group ();
2024 }
2025 
2026 /*********************************************************/
2027 /*******     GLADE_COMMAND_ADD_SIGNAL     *******/
2028 /*********************************************************/
2029 
2030 /* create a new GladeCommandAddRemoveChangeSignal class.  Objects of this class will
2031  * encapsulate an "add or remove signal handler" operation */
2032 typedef enum
2033 {
2034   GLADE_ADD,
2035   GLADE_REMOVE,
2036   GLADE_CHANGE
2037 } GladeAddType;
2038 
2039 typedef struct
2040 {
2041   GladeCommand parent;
2042 
2043   GladeWidget *widget;
2044 
2045   GladeSignal *signal;
2046   GladeSignal *new_signal;
2047 
2048   GladeAddType type;
2049 } GladeCommandAddSignal;
2050 
2051 /* standard macros */
2052 GLADE_MAKE_COMMAND (GladeCommandAddSignal, glade_command_add_signal);
2053 #define GLADE_COMMAND_ADD_SIGNAL_TYPE		(glade_command_add_signal_get_type ())
2054 #define GLADE_COMMAND_ADD_SIGNAL(o)	  	(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_ADD_SIGNAL_TYPE, GladeCommandAddSignal))
2055 #define GLADE_COMMAND_ADD_SIGNAL_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_ADD_SIGNAL_TYPE, GladeCommandAddSignalClass))
2056 #define GLADE_IS_COMMAND_ADD_SIGNAL(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_ADD_SIGNAL_TYPE))
2057 #define GLADE_IS_COMMAND_ADD_SIGNAL_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_ADD_SIGNAL_TYPE))
2058 
2059 static void
glade_command_add_signal_finalize(GObject * obj)2060 glade_command_add_signal_finalize (GObject *obj)
2061 {
2062   GladeCommandAddSignal *cmd = GLADE_COMMAND_ADD_SIGNAL (obj);
2063 
2064   g_object_unref (cmd->widget);
2065 
2066   if (cmd->signal)
2067     g_object_unref (cmd->signal);
2068   if (cmd->new_signal)
2069     g_object_unref (cmd->new_signal);
2070 
2071   glade_command_finalize (obj);
2072 }
2073 
2074 static gboolean
glade_command_add_signal_undo(GladeCommand * this_cmd)2075 glade_command_add_signal_undo (GladeCommand *this_cmd)
2076 {
2077   return glade_command_add_signal_execute (this_cmd);
2078 }
2079 
2080 static gboolean
glade_command_add_signal_execute(GladeCommand * this_cmd)2081 glade_command_add_signal_execute (GladeCommand *this_cmd)
2082 {
2083   GladeCommandAddSignal *cmd = GLADE_COMMAND_ADD_SIGNAL (this_cmd);
2084   GladeSignal *temp;
2085 
2086   switch (cmd->type)
2087     {
2088       case GLADE_ADD:
2089         glade_widget_add_signal_handler (cmd->widget, cmd->signal);
2090         cmd->type = GLADE_REMOVE;
2091         break;
2092       case GLADE_REMOVE:
2093         glade_widget_remove_signal_handler (cmd->widget, cmd->signal);
2094         cmd->type = GLADE_ADD;
2095         break;
2096       case GLADE_CHANGE:
2097         glade_widget_change_signal_handler (cmd->widget,
2098                                             cmd->signal, cmd->new_signal);
2099         temp = cmd->signal;
2100         cmd->signal = cmd->new_signal;
2101         cmd->new_signal = temp;
2102         break;
2103       default:
2104         break;
2105     }
2106   return TRUE;
2107 }
2108 
2109 static gboolean
glade_command_add_signal_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)2110 glade_command_add_signal_unifies (GladeCommand *this_cmd,
2111                                   GladeCommand *other_cmd)
2112 {
2113   return FALSE;
2114 }
2115 
2116 static void
glade_command_add_signal_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)2117 glade_command_add_signal_collapse (GladeCommand *this_cmd,
2118                                    GladeCommand *other_cmd)
2119 {
2120   g_return_if_reached ();
2121 }
2122 
2123 static void
glade_command_add_remove_change_signal(GladeWidget * glade_widget,const GladeSignal * signal,const GladeSignal * new_signal,GladeAddType type)2124 glade_command_add_remove_change_signal (GladeWidget *glade_widget,
2125                                         const GladeSignal *signal,
2126                                         const GladeSignal *new_signal,
2127                                         GladeAddType type)
2128 {
2129   GladeCommandAddSignal *me = GLADE_COMMAND_ADD_SIGNAL
2130       (g_object_new (GLADE_COMMAND_ADD_SIGNAL_TYPE, NULL));
2131   GladeCommand *cmd = GLADE_COMMAND (me);
2132 
2133   /* we can only add/remove a signal to a widget that has been wrapped by a GladeWidget */
2134   g_assert (glade_widget != NULL);
2135   g_assert (glade_widget_get_project (glade_widget) != NULL);
2136 
2137   me->widget = g_object_ref (glade_widget);
2138   me->type = type;
2139   me->signal = glade_signal_clone (signal);
2140   me->new_signal = new_signal ? glade_signal_clone (new_signal) : NULL;
2141 
2142   cmd->priv->project = glade_widget_get_project (glade_widget);
2143   cmd->priv->description =
2144       g_strdup_printf (type == GLADE_ADD ? _("Add signal handler %s") :
2145                        type == GLADE_REMOVE ? _("Remove signal handler %s") :
2146                        _("Change signal handler %s"),
2147 		       glade_signal_get_handler ((GladeSignal *)signal));
2148 
2149   glade_command_check_group (GLADE_COMMAND (me));
2150 
2151   if (glade_command_add_signal_execute (cmd))
2152     glade_project_push_undo (cmd->priv->project, cmd);
2153   else
2154     g_object_unref (G_OBJECT (me));
2155 }
2156 
2157 /**
2158  * glade_command_add_signal:
2159  * @glade_widget: a #GladeWidget
2160  * @signal: a #GladeSignal
2161  *
2162  * TODO: write me
2163  */
2164 void
glade_command_add_signal(GladeWidget * glade_widget,const GladeSignal * signal)2165 glade_command_add_signal (GladeWidget *glade_widget, const GladeSignal *signal)
2166 {
2167   glade_command_add_remove_change_signal
2168       (glade_widget, signal, NULL, GLADE_ADD);
2169 }
2170 
2171 /**
2172  * glade_command_remove_signal:
2173  * @glade_widget: a #GladeWidget
2174  * @signal: a #GladeSignal
2175  *
2176  * TODO: write me
2177  */
2178 void
glade_command_remove_signal(GladeWidget * glade_widget,const GladeSignal * signal)2179 glade_command_remove_signal (GladeWidget *glade_widget,
2180                              const GladeSignal *signal)
2181 {
2182   glade_command_add_remove_change_signal
2183       (glade_widget, signal, NULL, GLADE_REMOVE);
2184 }
2185 
2186 /**
2187  * glade_command_change_signal:
2188  * @glade_widget: a #GladeWidget
2189  * @old_signal: a #GladeSignal
2190  * @new_signal: a #GladeSignal
2191  *
2192  * TODO: write me
2193  */
2194 void
glade_command_change_signal(GladeWidget * glade_widget,const GladeSignal * old_signal,const GladeSignal * new_signal)2195 glade_command_change_signal (GladeWidget *glade_widget,
2196                              const GladeSignal *old_signal,
2197                              const GladeSignal *new_signal)
2198 {
2199   glade_command_add_remove_change_signal
2200       (glade_widget, old_signal, new_signal, GLADE_CHANGE);
2201 }
2202 
2203 /******************************************************************************
2204  *
2205  * set i18n metadata
2206  *
2207  * This command sets the i18n metadata on a label property.
2208  *
2209  *****************************************************************************/
2210 
2211 typedef struct
2212 {
2213   GladeCommand parent;
2214   GladeProperty *property;
2215   gboolean translatable;
2216   gchar *context;
2217   gchar *comment;
2218   gboolean old_translatable;
2219   gchar *old_context;
2220   gchar *old_comment;
2221 } GladeCommandSetI18n;
2222 
2223 
2224 GLADE_MAKE_COMMAND (GladeCommandSetI18n, glade_command_set_i18n);
2225 #define GLADE_COMMAND_SET_I18N_TYPE			(glade_command_set_i18n_get_type ())
2226 #define GLADE_COMMAND_SET_I18N(o)	  		(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_SET_I18N_TYPE, GladeCommandSetI18n))
2227 #define GLADE_COMMAND_SET_I18N_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_SET_I18N_TYPE, GladeCommandSetI18nClass))
2228 #define GLADE_IS_COMMAND_SET_I18N(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_SET_I18N_TYPE))
2229 #define GLADE_IS_COMMAND_SET_I18N_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_SET_I18N_TYPE))
2230 
2231 static gboolean
glade_command_set_i18n_execute(GladeCommand * cmd)2232 glade_command_set_i18n_execute (GladeCommand *cmd)
2233 {
2234   GladeCommandSetI18n *me = (GladeCommandSetI18n *) cmd;
2235   gboolean temp_translatable;
2236   gchar *temp_context;
2237   gchar *temp_comment;
2238 
2239   /* sanity check */
2240   g_return_val_if_fail (me != NULL, TRUE);
2241   g_return_val_if_fail (me->property != NULL, TRUE);
2242 
2243   /* set the new values in the property */
2244   glade_property_i18n_set_translatable (me->property, me->translatable);
2245   glade_property_i18n_set_context (me->property, me->context);
2246   glade_property_i18n_set_comment (me->property, me->comment);
2247 
2248   /* swap the current values with the old values to prepare for undo */
2249   temp_translatable = me->translatable;
2250   temp_context = me->context;
2251   temp_comment = me->comment;
2252   me->translatable = me->old_translatable;
2253   me->context = me->old_context;
2254   me->comment = me->old_comment;
2255   me->old_translatable = temp_translatable;
2256   me->old_context = temp_context;
2257   me->old_comment = temp_comment;
2258 
2259   return TRUE;
2260 }
2261 
2262 static gboolean
glade_command_set_i18n_undo(GladeCommand * cmd)2263 glade_command_set_i18n_undo (GladeCommand *cmd)
2264 {
2265   return glade_command_set_i18n_execute (cmd);
2266 }
2267 
2268 static void
glade_command_set_i18n_finalize(GObject * obj)2269 glade_command_set_i18n_finalize (GObject *obj)
2270 {
2271   GladeCommandSetI18n *me;
2272 
2273   g_return_if_fail (GLADE_IS_COMMAND_SET_I18N (obj));
2274 
2275   me = GLADE_COMMAND_SET_I18N (obj);
2276   g_free (me->context);
2277   g_free (me->comment);
2278   g_free (me->old_context);
2279   g_free (me->old_comment);
2280 
2281   glade_command_finalize (obj);
2282 }
2283 
2284 static gboolean
glade_command_set_i18n_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)2285 glade_command_set_i18n_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
2286 {
2287   GladeCommandSetI18n *cmd1;
2288   GladeCommandSetI18n *cmd2;
2289 
2290   if (GLADE_IS_COMMAND_SET_I18N (this_cmd) &&
2291       GLADE_IS_COMMAND_SET_I18N (other_cmd))
2292     {
2293       cmd1 = GLADE_COMMAND_SET_I18N (this_cmd);
2294       cmd2 = GLADE_COMMAND_SET_I18N (other_cmd);
2295 
2296       return (cmd1->property == cmd2->property);
2297     }
2298 
2299   return FALSE;
2300 }
2301 
2302 static void
glade_command_set_i18n_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)2303 glade_command_set_i18n_collapse (GladeCommand *this_cmd,
2304                                  GladeCommand *other_cmd)
2305 {
2306   /* this command is the one that will be used for an undo of the sequence of like commands */
2307   GladeCommandSetI18n *this = GLADE_COMMAND_SET_I18N (this_cmd);
2308 
2309   /* the other command contains the values that will be used for a redo */
2310   GladeCommandSetI18n *other = GLADE_COMMAND_SET_I18N (other_cmd);
2311 
2312   g_return_if_fail (GLADE_IS_COMMAND_SET_I18N (this_cmd) &&
2313                     GLADE_IS_COMMAND_SET_I18N (other_cmd));
2314 
2315   /* adjust this command to contain, as its old values, the other command's current values */
2316   this->old_translatable = other->old_translatable;
2317   g_free (this->old_context);
2318   g_free (this->old_comment);
2319   this->old_context = other->old_context;
2320   this->old_comment = other->old_comment;
2321   other->old_context = NULL;
2322   other->old_comment = NULL;
2323 }
2324 
2325 /**
2326  * glade_command_set_i18n:
2327  * @property: a #GladeProperty
2328  * @translatable: a #gboolean
2329  * @context: a #const gchar *
2330  * @comment: a #const gchar *
2331  *
2332  * Sets the i18n data on the property.
2333  */
2334 void
glade_command_set_i18n(GladeProperty * property,gboolean translatable,const gchar * context,const gchar * comment)2335 glade_command_set_i18n (GladeProperty *property,
2336                         gboolean translatable,
2337                         const gchar *context,
2338                         const gchar *comment)
2339 {
2340   GladeCommandSetI18n *me;
2341 
2342   g_return_if_fail (property);
2343 
2344   /* check that something changed before continuing with the command */
2345   if (translatable == glade_property_i18n_get_translatable (property) &&
2346       !g_strcmp0 (glade_property_i18n_get_context (property), context) &&
2347       !g_strcmp0 (glade_property_i18n_get_comment (property), comment))
2348     return;
2349 
2350   /* load up the command */
2351   me = g_object_new (GLADE_COMMAND_SET_I18N_TYPE, NULL);
2352   me->property = property;
2353   me->translatable = translatable;
2354   me->context = g_strdup (context);
2355   me->comment = g_strdup (comment);
2356   me->old_translatable = glade_property_i18n_get_translatable (property);
2357   me->old_context = g_strdup (glade_property_i18n_get_context (property));
2358   me->old_comment = g_strdup (glade_property_i18n_get_comment (property));
2359 
2360   GLADE_COMMAND (me)->priv->project =
2361     glade_widget_get_project (glade_property_get_widget (property));
2362   GLADE_COMMAND (me)->priv->description =
2363       g_strdup_printf (_("Setting i18n metadata"));;
2364 
2365   glade_command_check_group (GLADE_COMMAND (me));
2366 
2367   /* execute the command and push it on the stack if successful */
2368   if (glade_command_set_i18n_execute (GLADE_COMMAND (me)))
2369     glade_project_push_undo (GLADE_COMMAND (me)->priv->project, GLADE_COMMAND (me));
2370   else
2371     g_object_unref (G_OBJECT (me));
2372 }
2373 
2374 /******************************************************************************
2375  *
2376  * This command sets protection warnings on widgets
2377  *
2378  *****************************************************************************/
2379 
2380 typedef struct
2381 {
2382   GladeCommand parent;
2383   GladeWidget *widget;
2384   GladeWidget *locked;
2385   gboolean locking;
2386 } GladeCommandLock;
2387 
2388 
2389 GLADE_MAKE_COMMAND (GladeCommandLock, glade_command_lock);
2390 #define GLADE_COMMAND_LOCK_TYPE			(glade_command_lock_get_type ())
2391 #define GLADE_COMMAND_LOCK(o)	  		(G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_LOCK_TYPE, GladeCommandLock))
2392 #define GLADE_COMMAND_LOCK_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_LOCK_TYPE, GladeCommandLockClass))
2393 #define GLADE_IS_COMMAND_LOCK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_LOCK_TYPE))
2394 #define GLADE_IS_COMMAND_LOCK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_LOCK_TYPE))
2395 
2396 static gboolean
glade_command_lock_execute(GladeCommand * cmd)2397 glade_command_lock_execute (GladeCommand *cmd)
2398 {
2399   GladeCommandLock *me = (GladeCommandLock *) cmd;
2400 
2401   /* set the new policy */
2402   if (me->locking)
2403     glade_widget_lock (me->widget, me->locked);
2404   else
2405     glade_widget_unlock (me->locked);
2406 
2407   /* swap the current values with the old values to prepare for undo */
2408   me->locking = !me->locking;
2409 
2410   return TRUE;
2411 }
2412 
2413 static gboolean
glade_command_lock_undo(GladeCommand * cmd)2414 glade_command_lock_undo (GladeCommand *cmd)
2415 {
2416   return glade_command_lock_execute (cmd);
2417 }
2418 
2419 static void
glade_command_lock_finalize(GObject * obj)2420 glade_command_lock_finalize (GObject *obj)
2421 {
2422   GladeCommandLock *me = (GladeCommandLock *) obj;
2423 
2424   g_object_unref (me->widget);
2425   g_object_unref (me->locked);
2426 
2427   glade_command_finalize (obj);
2428 }
2429 
2430 static gboolean
glade_command_lock_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)2431 glade_command_lock_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
2432 {
2433 /* 	GladeCommandLock *cmd1; */
2434 /* 	GladeCommandLock *cmd2; */
2435   /* No point here, this command undoubtedly always runs in groups */
2436   return FALSE;
2437 }
2438 
2439 static void
glade_command_lock_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)2440 glade_command_lock_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd)
2441 {
2442   /* this command is the one that will be used for an undo of the sequence of like commands */
2443   //GladeCommandLock *this = GLADE_COMMAND_LOCK (this_cmd);
2444 
2445   /* the other command contains the values that will be used for a redo */
2446   //GladeCommandLock *other = GLADE_COMMAND_LOCK (other_cmd);
2447 
2448   g_return_if_fail (GLADE_IS_COMMAND_LOCK (this_cmd) &&
2449                     GLADE_IS_COMMAND_LOCK (other_cmd));
2450 
2451   /* no unify/collapse */
2452 }
2453 
2454 /**
2455  * glade_command_lock_widget:
2456  * @widget: A #GladeWidget
2457  * @locked: The #GladeWidget to lock
2458  *
2459  * Sets @locked to be in a locked up state
2460  * spoken for by @widget, locked widgets cannot
2461  * be removed from the project until unlocked.
2462  */
2463 void
glade_command_lock_widget(GladeWidget * widget,GladeWidget * locked)2464 glade_command_lock_widget (GladeWidget *widget, GladeWidget *locked)
2465 {
2466   GladeCommandLock *me;
2467 
2468   g_return_if_fail (GLADE_IS_WIDGET (widget));
2469   g_return_if_fail (GLADE_IS_WIDGET (locked));
2470   g_return_if_fail (glade_widget_get_locker (locked) == NULL);
2471 
2472   /* load up the command */
2473   me = g_object_new (GLADE_COMMAND_LOCK_TYPE, NULL);
2474   me->widget = g_object_ref (widget);
2475   me->locked = g_object_ref (locked);
2476   me->locking = TRUE;
2477 
2478   GLADE_COMMAND (me)->priv->project = glade_widget_get_project (widget);
2479   GLADE_COMMAND (me)->priv->description =
2480     g_strdup_printf (_("Locking %s by widget %s"),
2481 		     glade_widget_get_name (locked),
2482 		     glade_widget_get_name (widget));
2483 
2484   glade_command_check_group (GLADE_COMMAND (me));
2485 
2486   /* execute the command and push it on the stack if successful
2487    * this sets the actual policy
2488    */
2489   if (glade_command_lock_execute (GLADE_COMMAND (me)))
2490     glade_project_push_undo (GLADE_COMMAND (me)->priv->project, GLADE_COMMAND (me));
2491   else
2492     g_object_unref (G_OBJECT (me));
2493 
2494 }
2495 
2496 
2497 /**
2498  * glade_command_unlock_widget:
2499  * @widget: A #GladeWidget
2500  *
2501  * Unlocks @widget so that it can be removed
2502  * from the project again
2503  *
2504  */
2505 void
glade_command_unlock_widget(GladeWidget * widget)2506 glade_command_unlock_widget (GladeWidget *widget)
2507 {
2508   GladeCommandLock *me;
2509 
2510   g_return_if_fail (GLADE_IS_WIDGET (widget));
2511   g_return_if_fail (GLADE_IS_WIDGET (glade_widget_get_locker (widget)));
2512 
2513   /* load up the command */
2514   me = g_object_new (GLADE_COMMAND_LOCK_TYPE, NULL);
2515   me->widget = g_object_ref (glade_widget_get_locker (widget));
2516   me->locked = g_object_ref (widget);
2517   me->locking = FALSE;
2518 
2519   GLADE_COMMAND (me)->priv->project = glade_widget_get_project (widget);
2520   GLADE_COMMAND (me)->priv->description =
2521     g_strdup_printf (_("Unlocking %s"), glade_widget_get_name (widget));
2522 
2523   glade_command_check_group (GLADE_COMMAND (me));
2524 
2525   /* execute the command and push it on the stack if successful
2526    * this sets the actual policy
2527    */
2528   if (glade_command_lock_execute (GLADE_COMMAND (me)))
2529     glade_project_push_undo (GLADE_COMMAND (me)->priv->project, GLADE_COMMAND (me));
2530   else
2531     g_object_unref (G_OBJECT (me));
2532 
2533 }
2534 
2535 
2536 /******************************************************************************
2537  *
2538  * This command sets the target version of a GladeProject
2539  *
2540  *****************************************************************************/
2541 typedef struct
2542 {
2543   GladeCommand parent;
2544   gchar       *catalog;
2545   gint         old_major;
2546   gint         old_minor;
2547   gint         new_major;
2548   gint         new_minor;
2549 } GladeCommandTarget;
2550 
2551 GLADE_MAKE_COMMAND (GladeCommandTarget, glade_command_target);
2552 #define GLADE_COMMAND_TARGET_TYPE	 (glade_command_target_get_type ())
2553 #define GLADE_COMMAND_TARGET(o)	  	 (G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_TARGET_TYPE, GladeCommandTarget))
2554 #define GLADE_COMMAND_TARGET_CLASS(k)	 (G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_TARGET_TYPE, GladeCommandTargetClass))
2555 #define GLADE_IS_COMMAND_TARGET(o)	 (G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_TARGET_TYPE))
2556 #define GLADE_IS_COMMAND_TARGET_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_TARGET_TYPE))
2557 
2558 static gboolean
glade_command_target_execute(GladeCommand * cmd)2559 glade_command_target_execute (GladeCommand *cmd)
2560 {
2561   GladeCommandTarget *me = (GladeCommandTarget *) cmd;
2562 
2563   glade_project_set_target_version (cmd->priv->project,
2564 				    me->catalog,
2565 				    me->new_major,
2566 				    me->new_minor);
2567 
2568   return TRUE;
2569 }
2570 
2571 static gboolean
glade_command_target_undo(GladeCommand * cmd)2572 glade_command_target_undo (GladeCommand *cmd)
2573 {
2574   GladeCommandTarget *me = (GladeCommandTarget *) cmd;
2575 
2576   glade_project_set_target_version (cmd->priv->project,
2577 				    me->catalog,
2578 				    me->old_major,
2579 				    me->old_minor);
2580 
2581   return TRUE;
2582 }
2583 
2584 static void
glade_command_target_finalize(GObject * obj)2585 glade_command_target_finalize (GObject *obj)
2586 {
2587   GladeCommandTarget *me = (GladeCommandTarget *) obj;
2588 
2589   g_free (me->catalog);
2590 
2591   glade_command_finalize (obj);
2592 }
2593 
2594 static gboolean
glade_command_target_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)2595 glade_command_target_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
2596 {
2597   GladeCommandTarget *me;
2598 
2599   /* Do we unify with self ? */
2600   if (!other_cmd)
2601     {
2602       if (GLADE_IS_COMMAND_TARGET (this_cmd))
2603         {
2604           me = (GladeCommandTarget *) this_cmd;
2605 
2606 	  return (me->old_major == me->new_major &&
2607 		  me->old_minor == me->new_minor);
2608         }
2609       return FALSE;
2610     }
2611 
2612   if (GLADE_IS_COMMAND_TARGET (this_cmd) &&
2613       GLADE_IS_COMMAND_TARGET (other_cmd))
2614     {
2615       GladeCommandTarget *other;
2616 
2617       me = (GladeCommandTarget *) this_cmd;
2618       other = (GladeCommandTarget *) other_cmd;
2619 
2620       return g_strcmp0 (me->catalog, other->catalog) == 0;
2621     }
2622 
2623   return FALSE;
2624 }
2625 
2626 static void
glade_command_target_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)2627 glade_command_target_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd)
2628 {
2629   GladeCommandTarget *this;
2630   GladeCommandTarget *other;
2631 
2632   g_return_if_fail (GLADE_IS_COMMAND_TARGET (this_cmd) &&
2633                     GLADE_IS_COMMAND_TARGET (other_cmd));
2634 
2635   this = GLADE_COMMAND_TARGET (this_cmd);
2636   other = GLADE_COMMAND_TARGET (other_cmd);
2637 
2638   this->new_major = other->new_major;
2639   this->new_minor = other->new_minor;
2640 
2641   g_free (this_cmd->priv->description);
2642   this_cmd->priv->description =
2643     g_strdup_printf (_("Setting target version of '%s' to %d.%d"),
2644 		     this->catalog, this->new_major, this->new_minor);
2645 
2646 }
2647 
2648 /**
2649  * glade_command_set_project_target:
2650  * @project: A #GladeProject
2651  * @catalog: The name of the catalog to set the project's target for
2652  * @major: The new major version of @catalog to target
2653  * @minor: The new minor version of @catalog to target
2654  *
2655  * Sets the target of @catalog to @major.@minor in @project.
2656  */
2657 void
glade_command_set_project_target(GladeProject * project,const gchar * catalog,gint major,gint minor)2658 glade_command_set_project_target  (GladeProject *project,
2659 				   const gchar  *catalog,
2660 				   gint          major,
2661 				   gint          minor)
2662 {
2663   GladeCommandTarget *me;
2664   gint old_major = 0;
2665   gint old_minor = 0;
2666 
2667   g_return_if_fail (GLADE_IS_PROJECT (project));
2668   g_return_if_fail (catalog && catalog[0]);
2669   g_return_if_fail (major >= 0);
2670   g_return_if_fail (minor >= 0);
2671 
2672   /* load up the command */
2673   me = g_object_new (GLADE_COMMAND_TARGET_TYPE, NULL);
2674   GLADE_COMMAND (me)->priv->project = project;
2675 
2676   me->catalog = g_strdup (catalog);
2677 
2678   glade_project_get_target_version (project, me->catalog, &old_major, &old_minor);
2679 
2680   me->new_major = major;
2681   me->new_minor = minor;
2682   me->old_major = old_major;
2683   me->old_minor = old_minor;
2684 
2685   GLADE_COMMAND (me)->priv->description =
2686     g_strdup_printf (_("Setting target version of '%s' to %d.%d"),
2687 		     me->catalog, me->new_major, me->new_minor);
2688 
2689   glade_command_check_group (GLADE_COMMAND (me));
2690 
2691   /* execute the command and push it on the stack if successful
2692    * this sets the actual policy
2693    */
2694   if (glade_command_target_execute (GLADE_COMMAND (me)))
2695     glade_project_push_undo (GLADE_COMMAND (me)->priv->project, GLADE_COMMAND (me));
2696   else
2697     g_object_unref (G_OBJECT (me));
2698 }
2699 
2700 /******************************************************************************
2701  *
2702  * This command sets can set different properties of a GladeProject
2703  *
2704  *****************************************************************************/
2705 typedef gchar *(*DescriptionNewFunc) (GladeCommand *);
2706 
2707 typedef struct
2708 {
2709   GladeCommand parent;
2710 
2711   const gchar *property_id;            /* Intern string */
2712   DescriptionNewFunc description_new;  /* Used to update command description */
2713   GValue old_value;
2714   GValue new_value;
2715 } GladeCommandProperty;
2716 
2717 GLADE_MAKE_COMMAND (GladeCommandProperty, glade_command_property);
2718 #define GLADE_COMMAND_PROPERTY_TYPE	   (glade_command_property_get_type ())
2719 #define GLADE_COMMAND_PROPERTY(o)	   (G_TYPE_CHECK_INSTANCE_CAST ((o), GLADE_COMMAND_PROPERTY_TYPE, GladeCommandProperty))
2720 #define GLADE_COMMAND_PROPERTY_CLASS(k)	   (G_TYPE_CHECK_CLASS_CAST ((k), GLADE_COMMAND_PROPERTY_TYPE, GladeCommandPropertyClass))
2721 #define GLADE_IS_COMMAND_PROPERTY(o)	   (G_TYPE_CHECK_INSTANCE_TYPE ((o), GLADE_COMMAND_PROPERTY_TYPE))
2722 #define GLADE_IS_COMMAND_PROPERTY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GLADE_COMMAND_PROPERTY_TYPE))
2723 
2724 /* Return true if a == b, this could be exported in glade_utils */
2725 static gboolean
glade_command_property_compare(GValue * a,GValue * b)2726 glade_command_property_compare (GValue *a, GValue *b)
2727 {
2728   if (G_VALUE_TYPE (a) != G_VALUE_TYPE (b))
2729     {
2730       g_warning ("Comparing a %s with a %s type is not supported",
2731                  G_VALUE_TYPE_NAME (a), G_VALUE_TYPE_NAME (b));
2732       return FALSE;
2733     }
2734 
2735   if (G_VALUE_HOLDS_STRING (a))
2736     return g_strcmp0 (g_value_get_string (a), g_value_get_string (b)) == 0;
2737   else if (G_VALUE_HOLDS_OBJECT (a))
2738     return g_value_get_object (a) == g_value_get_object (b);
2739   else if (G_VALUE_HOLDS_BOOLEAN (a))
2740     return g_value_get_boolean (a) == g_value_get_boolean (b);
2741   else if (G_VALUE_HOLDS_CHAR (a))
2742     return g_value_get_schar (a) == g_value_get_schar (b);
2743   else if (G_VALUE_HOLDS_DOUBLE (a))
2744     return g_value_get_double (a) == g_value_get_double (b);
2745   else if (G_VALUE_HOLDS_ENUM (a))
2746     return g_value_get_enum (a) == g_value_get_enum (b);
2747   else if (G_VALUE_HOLDS_FLAGS (a))
2748     return g_value_get_flags (a) == g_value_get_flags (b);
2749   else if (G_VALUE_HOLDS_FLOAT (a))
2750     return g_value_get_float (a) == g_value_get_float (b);
2751   else if (G_VALUE_HOLDS_GTYPE (a))
2752     return g_value_get_gtype (a) == g_value_get_gtype (b);
2753   else if (G_VALUE_HOLDS_INT (a))
2754     return g_value_get_int (a) == g_value_get_int (b);
2755   else if (G_VALUE_HOLDS_INT64 (a))
2756     return g_value_get_int64 (a) == g_value_get_int64 (b);
2757   else if (G_VALUE_HOLDS_LONG (a))
2758     return g_value_get_long (a) == g_value_get_long (b);
2759   else if (G_VALUE_HOLDS_POINTER (a))
2760     return g_value_get_pointer (a) == g_value_get_pointer (b);
2761   else if (G_VALUE_HOLDS_UCHAR (a))
2762     return g_value_get_uchar (a) == g_value_get_uchar (b);
2763   else if (G_VALUE_HOLDS_UINT (a))
2764     return g_value_get_uint (a) == g_value_get_uint (b);
2765   else if (G_VALUE_HOLDS_UINT64 (a))
2766     return g_value_get_uint64 (a) == g_value_get_uint64 (b);
2767   else if (G_VALUE_HOLDS_ULONG (a))
2768     return g_value_get_ulong (a) == g_value_get_ulong (b);
2769 
2770   g_warning ("%s type not supported", G_VALUE_TYPE_NAME (a));
2771   return FALSE;
2772 }
2773 
2774 static gboolean
glade_command_property_execute(GladeCommand * cmd)2775 glade_command_property_execute (GladeCommand *cmd)
2776 {
2777   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
2778   g_object_set_property (G_OBJECT (cmd->priv->project), me->property_id, &me->new_value);
2779   return TRUE;
2780 }
2781 
2782 static gboolean
glade_command_property_undo(GladeCommand * cmd)2783 glade_command_property_undo (GladeCommand *cmd)
2784 {
2785   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
2786   g_object_set_property (G_OBJECT (cmd->priv->project), me->property_id, &me->old_value);
2787   return TRUE;
2788 }
2789 
2790 static void
glade_command_property_finalize(GObject * obj)2791 glade_command_property_finalize (GObject *obj)
2792 {
2793   GladeCommandProperty *me = (GladeCommandProperty *) obj;
2794 
2795   /* NOTE: we do not free me->property_id because it is an intern string */
2796   g_value_unset (&me->new_value);
2797   g_value_unset (&me->old_value);
2798 
2799   glade_command_finalize (obj);
2800 }
2801 
2802 static gboolean
glade_command_property_unifies(GladeCommand * this_cmd,GladeCommand * other_cmd)2803 glade_command_property_unifies (GladeCommand *this_cmd, GladeCommand *other_cmd)
2804 {
2805   /* Do we unify with self ? */
2806   if (!other_cmd)
2807     {
2808       if (GLADE_IS_COMMAND_PROPERTY (this_cmd))
2809         {
2810           GladeCommandProperty *me = (GladeCommandProperty *) this_cmd;
2811           return glade_command_property_compare (&me->new_value, &me->old_value);
2812         }
2813       else
2814         return FALSE;
2815     }
2816 
2817   if (GLADE_IS_COMMAND_PROPERTY (this_cmd) && GLADE_IS_COMMAND_PROPERTY (other_cmd))
2818     {
2819       GladeCommandProperty *this = (GladeCommandProperty *) this_cmd;
2820       GladeCommandProperty *other = (GladeCommandProperty *) other_cmd;
2821 
2822       /* Intern strings can be compared by comparind the pointers */
2823       return this->property_id == other->property_id;
2824     }
2825 
2826   return FALSE;
2827 }
2828 
2829 static void
glade_command_property_update_description(GladeCommand * cmd)2830 glade_command_property_update_description (GladeCommand *cmd)
2831 {
2832   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
2833 
2834   g_free (cmd->priv->description);
2835 
2836   if (me->description_new)
2837     cmd->priv->description = me->description_new (cmd);
2838   else
2839     cmd->priv->description = g_strdup_printf (_("Setting project's %s property"),
2840                                               me->property_id);
2841 }
2842 
2843 static void
glade_command_property_collapse(GladeCommand * this_cmd,GladeCommand * other_cmd)2844 glade_command_property_collapse (GladeCommand *this_cmd, GladeCommand *other_cmd)
2845 {
2846   GladeCommandProperty *this;
2847   GladeCommandProperty *other;
2848 
2849   g_return_if_fail (GLADE_IS_COMMAND_PROPERTY (this_cmd) &&
2850                     GLADE_IS_COMMAND_PROPERTY (other_cmd));
2851 
2852   this = GLADE_COMMAND_PROPERTY (this_cmd);
2853   other = GLADE_COMMAND_PROPERTY (other_cmd);
2854 
2855   g_return_if_fail (this->property_id == other->property_id);
2856 
2857   g_value_copy (&other->new_value, &this->new_value);
2858 
2859   glade_command_property_update_description (this_cmd);
2860 }
2861 
2862 /**
2863  * glade_command_set_project_property:
2864  * @project: A #GladeProject
2865  * @description_new: function to create the command description.
2866  * @property_id: property this command should use
2867  * @new_value: the value to set @property_id
2868  *
2869  * Sets @new_value as the @property_id property for @project.
2870  */
2871 static void
glade_command_set_project_property(GladeProject * project,DescriptionNewFunc description_new,const gchar * property_id,GValue * new_value)2872 glade_command_set_project_property (GladeProject       *project,
2873                                     DescriptionNewFunc  description_new,
2874 				    const gchar        *property_id,
2875                                     GValue             *new_value)
2876 {
2877   GladeCommandProperty *me;
2878   GValue old_value = G_VALUE_INIT;
2879 
2880   g_value_init (&old_value, G_VALUE_TYPE (new_value));
2881   g_object_get_property (G_OBJECT (project), property_id, &old_value);
2882 
2883   if (glade_command_property_compare (&old_value, new_value))
2884     {
2885       g_value_unset (&old_value);
2886       return;
2887     }
2888 
2889   me = g_object_new (GLADE_COMMAND_PROPERTY_TYPE, NULL);
2890   GLADE_COMMAND (me)->priv->project = project;
2891 
2892   me->description_new = description_new;
2893   me->property_id = g_intern_static_string (property_id);
2894 
2895   /* move the old value to the comand struct */
2896   me->old_value = old_value;
2897 
2898   /* set new value */
2899   g_value_init (&me->new_value, G_VALUE_TYPE (new_value));
2900   g_value_copy (new_value, &me->new_value);
2901 
2902   glade_command_property_update_description (GLADE_COMMAND (me));
2903 
2904   glade_command_check_group (GLADE_COMMAND (me));
2905 
2906   /* execute the command and push it on the stack if successful
2907    * this sets the actual policy
2908    */
2909   if (glade_command_property_execute (GLADE_COMMAND (me)))
2910     glade_project_push_undo (GLADE_COMMAND (me)->priv->project, GLADE_COMMAND (me));
2911   else
2912     g_object_unref (G_OBJECT (me));
2913 }
2914 
2915 /**
2916  * glade_command_set_project_license:
2917  * @project: A #GladeProject
2918  * @license: License of @project
2919  *
2920  * Sets the license agreement for @project. It will be saved in the xml as comment.
2921  */
2922 void
glade_command_set_project_license(GladeProject * project,const gchar * license)2923 glade_command_set_project_license (GladeProject *project, const gchar *license)
2924 {
2925   GValue new_value = G_VALUE_INIT;
2926 
2927   g_return_if_fail (GLADE_IS_PROJECT (project));
2928 
2929   g_value_init (&new_value, G_TYPE_STRING);
2930   g_value_set_string (&new_value, license);
2931 
2932   glade_command_set_project_property (project, NULL, "license", &new_value);
2933 
2934   g_value_unset (&new_value);
2935 }
2936 
2937 static gchar *
gcp_resource_path_description_new(GladeCommand * cmd)2938 gcp_resource_path_description_new (GladeCommand *cmd)
2939 {
2940   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
2941 
2942   return g_strdup_printf (_("Setting resource path to '%s'"),
2943                           g_value_get_string (&me->new_value));
2944 }
2945 
2946 /**
2947  * glade_command_set_project_resource_path:
2948  * @project: A #GladeProject
2949  * @path: path to load resources from.
2950  *
2951  * Sets a resource path @project.
2952  */
2953 void
glade_command_set_project_resource_path(GladeProject * project,const gchar * path)2954 glade_command_set_project_resource_path (GladeProject *project, const gchar *path)
2955 {
2956   GValue new_value = G_VALUE_INIT;
2957 
2958   g_return_if_fail (GLADE_IS_PROJECT (project));
2959 
2960   g_value_init (&new_value, G_TYPE_STRING);
2961   g_value_set_string (&new_value, path);
2962 
2963   glade_command_set_project_property (project, gcp_resource_path_description_new,
2964                                       "resource-path", &new_value);
2965   g_value_unset (&new_value);
2966 }
2967 
2968 static gchar *
gcp_domain_description_new(GladeCommand * cmd)2969 gcp_domain_description_new (GladeCommand *cmd)
2970 {
2971   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
2972 
2973   return g_strdup_printf (_("Setting translation domain to '%s'"),
2974                           g_value_get_string (&me->new_value));
2975 }
2976 
2977 /**
2978  * glade_command_set_project_domain:
2979  * @project: A #GladeProject
2980  * @domain: The translation domain for @project
2981  *
2982  * Sets @domain as the translation domain for @project.
2983  */
2984 void
glade_command_set_project_domain(GladeProject * project,const gchar * domain)2985 glade_command_set_project_domain  (GladeProject *project,
2986 				   const gchar  *domain)
2987 {
2988   GValue new_value = G_VALUE_INIT;
2989 
2990   g_return_if_fail (GLADE_IS_PROJECT (project));
2991 
2992   g_value_init (&new_value, G_TYPE_STRING);
2993   g_value_set_string (&new_value, domain);
2994 
2995   glade_command_set_project_property (project, gcp_domain_description_new,
2996                                       "translation-domain", &new_value);
2997   g_value_unset (&new_value);
2998 }
2999 
3000 static gchar *
gcp_template_description_new(GladeCommand * cmd)3001 gcp_template_description_new (GladeCommand *cmd)
3002 {
3003   GladeCommandProperty *me = (GladeCommandProperty *) cmd;
3004   GObject *new_template = g_value_get_object (&me->new_value);
3005   GObject *old_template = g_value_get_object (&me->old_value);
3006 
3007   if (new_template == NULL && old_template != NULL)
3008     return g_strdup_printf (_("Unsetting widget '%s' as template"),
3009                             glade_widget_get_name (GLADE_WIDGET (old_template)));
3010   else if (new_template != NULL)
3011     return g_strdup_printf (_("Setting widget '%s' as template"),
3012                             glade_widget_get_name (GLADE_WIDGET (new_template)));
3013   else
3014     return g_strdup (_("Unsetting template"));
3015 }
3016 
3017 /**
3018  * glade_command_set_project_template:
3019  * @project: A #GladeProject
3020  * @widget: The #GladeWidget to make template
3021  *
3022  * Sets @widget to be the template widget in @project.
3023  */
3024 void
glade_command_set_project_template(GladeProject * project,GladeWidget * widget)3025 glade_command_set_project_template (GladeProject *project,
3026 				    GladeWidget  *widget)
3027 {
3028   GValue new_value = G_VALUE_INIT;
3029 
3030   g_return_if_fail (GLADE_IS_PROJECT (project));
3031 
3032   g_value_init (&new_value, G_TYPE_OBJECT);
3033   g_value_set_object (&new_value, widget);
3034 
3035   glade_command_set_project_property (project, gcp_template_description_new,
3036                                       "template", &new_value);
3037   g_value_unset (&new_value);
3038 }
3039