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