1 /*
2  * glade-gtk-table.c - GladeWidgetAdaptor for GtkTable widget
3  *
4  * Copyright (C) 2008 Tristan Van Berkom
5  *
6  * Author(s):
7  *      Tristan Van Berkom <tvb@gnome.org>
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as
11  * published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 #include <config.h>
25 
26 #include <gtk/gtk.h>
27 #include <glib/gi18n-lib.h>
28 #include <string.h>
29 
30 #include "glade-fixed.h"
31 
32 typedef struct
33 {
34   /* comparable part: */
35   GladeWidget *widget;
36   gint left_attach;
37   gint right_attach;
38   gint top_attach;
39   gint bottom_attach;
40 } GladeGtkTableChild;
41 
42 typedef enum
43 {
44   DIR_UP,
45   DIR_DOWN,
46   DIR_LEFT,
47   DIR_RIGHT
48 } GladeTableDir;
49 
50 typedef enum
51 {
52   GROUP_ACTION_INSERT_ROW,
53   GROUP_ACTION_INSERT_COLUMN,
54   GROUP_ACTION_REMOVE_COLUMN,
55   GROUP_ACTION_REMOVE_ROW
56 } GroupAction;
57 
58 /* Redefine GTK_TABLE() macro, as GtkTable is deprecated */
59 #undef GTK_TABLE
60 #define GTK_TABLE(obj) ((GtkTable *)obj)
61 
62 static void
glade_gtk_table_get_child_attachments(GtkWidget * table,GtkWidget * child,GtkTableChild * tchild)63 glade_gtk_table_get_child_attachments (GtkWidget * table,
64                                        GtkWidget * child,
65                                        GtkTableChild * tchild)
66 {
67   guint left, right, top, bottom;
68 
69   gtk_container_child_get (GTK_CONTAINER (table), child,
70                            "left-attach", (guint *) & left,
71                            "right-attach", (guint *) & right,
72                            "bottom-attach", (guint *) & bottom,
73                            "top-attach", (guint *) & top, NULL);
74 
75   tchild->widget = child;
76   tchild->left_attach = left;
77   tchild->right_attach = right;
78   tchild->top_attach = top;
79   tchild->bottom_attach = bottom;
80 }
81 
82 static gboolean
glade_gtk_table_widget_exceeds_bounds(GtkTable * table,gint n_rows,gint n_cols)83 glade_gtk_table_widget_exceeds_bounds (GtkTable * table, gint n_rows,
84                                        gint n_cols)
85 {
86   GList *list, *children;
87   gboolean ret = FALSE;
88 
89   children = gtk_container_get_children (GTK_CONTAINER (table));
90 
91   for (list = children; list && list->data; list = list->next)
92     {
93       GtkTableChild child;
94 
95       glade_gtk_table_get_child_attachments (GTK_WIDGET (table),
96                                              GTK_WIDGET (list->data), &child);
97 
98       if (GLADE_IS_PLACEHOLDER (child.widget) == FALSE &&
99           (child.right_attach > n_cols || child.bottom_attach > n_rows))
100         {
101           ret = TRUE;
102           break;
103         }
104     }
105 
106   g_list_free (children);
107 
108   return ret;
109 }
110 
111 #define TABLE_OCCUPIED(occmap, n_columns, col, row) \
112     (occmap)[row * n_columns + col]
113 
114 static void
glade_gtk_table_build_occupation_maps(GtkTable * table,guint n_columns,guint n_rows,gchar ** child_map,gpointer ** placeholder_map)115 glade_gtk_table_build_occupation_maps(GtkTable *table, guint n_columns, guint n_rows,
116 				      gchar **child_map, gpointer **placeholder_map)
117 {
118     guint i, j;
119     GList *list, *children = gtk_container_get_children (GTK_CONTAINER (table));
120 
121     *child_map = g_malloc0(n_columns * n_rows * sizeof(gchar));  /* gchar is smaller than gboolean */
122     *placeholder_map = g_malloc0(n_columns * n_rows * sizeof(gpointer));
123 
124     for (list = children; list && list->data; list = list->next)
125     {
126 	GtkTableChild child;
127 
128 	glade_gtk_table_get_child_attachments (GTK_WIDGET (table),
129 					       GTK_WIDGET (list->data), &child);
130 
131 	if (GLADE_IS_PLACEHOLDER(list->data))
132 	{
133 	    /* assumption: placeholders are always attached to exactly 1 cell */
134 	    TABLE_OCCUPIED(*placeholder_map, n_columns, child.left_attach, child.top_attach) = list->data;
135 	}
136 	else
137 	{
138 	    for (i = child.left_attach; i < child.right_attach && i < n_columns; i++)
139 	    {
140 		for (j = child.top_attach; j < child.bottom_attach && j < n_rows; j++)
141 		{
142 		    TABLE_OCCUPIED(*child_map, n_columns, i, j) = 1;
143 		}
144 	    }
145 	}
146     }
147     g_list_free (children);
148 }
149 
150 static void
glade_gtk_table_refresh_placeholders(GtkTable * table)151 glade_gtk_table_refresh_placeholders (GtkTable * table)
152 {
153   guint n_columns, n_rows, i, j;
154   gchar *child_map;
155   gpointer *placeholder_map;
156 
157   g_object_get (table, "n-columns", &n_columns, "n-rows", &n_rows, NULL);
158   glade_gtk_table_build_occupation_maps (table, n_columns, n_rows,
159 					 &child_map, &placeholder_map);
160 
161   for (i = 0; i < n_columns; i++)
162     {
163       for (j = 0; j < n_rows; j++)
164 	{
165 	  gpointer placeholder = TABLE_OCCUPIED(placeholder_map, n_columns, i, j);
166 
167 	  if (TABLE_OCCUPIED(child_map, n_columns, i, j))
168 	    {
169 	      if (placeholder)
170 		{
171 		  gtk_container_remove (GTK_CONTAINER (table),
172 					GTK_WIDGET (placeholder));
173 		}
174 	    }
175 	  else
176 	    {
177 	      if (!placeholder)
178 		{
179 		  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
180 		  gtk_table_attach_defaults (table,
181 					     glade_placeholder_new (),
182 					     i, i + 1, j, j + 1);
183 		  G_GNUC_END_IGNORE_DEPRECATIONS;
184 		}
185 	    }
186 	}
187     }
188   g_free(child_map);
189   g_free(placeholder_map);
190 
191   if (gtk_widget_get_realized (GTK_WIDGET (table)))
192     gtk_container_check_resize (GTK_CONTAINER (table));
193 }
194 
195 static void
gtk_table_children_callback(GtkWidget * widget,gpointer client_data)196 gtk_table_children_callback (GtkWidget * widget, gpointer client_data)
197 {
198   GList **children;
199 
200   children = (GList **) client_data;
201   *children = g_list_prepend (*children, widget);
202 }
203 
204 GList *
glade_gtk_table_get_children(GladeWidgetAdaptor * adaptor,GtkContainer * container)205 glade_gtk_table_get_children (GladeWidgetAdaptor * adaptor,
206                               GtkContainer * container)
207 {
208   GList *children = NULL;
209 
210   gtk_container_forall (container, gtk_table_children_callback, &children);
211 
212   /* GtkTable has the children list already reversed */
213   return children;
214 }
215 
216 void
glade_gtk_table_add_child(GladeWidgetAdaptor * adaptor,GObject * object,GObject * child)217 glade_gtk_table_add_child (GladeWidgetAdaptor * adaptor,
218                            GObject * object, GObject * child)
219 {
220   gtk_container_add (GTK_CONTAINER (object), GTK_WIDGET (child));
221 
222   glade_gtk_table_refresh_placeholders (GTK_TABLE (object));
223 }
224 
225 void
glade_gtk_table_remove_child(GladeWidgetAdaptor * adaptor,GObject * object,GObject * child)226 glade_gtk_table_remove_child (GladeWidgetAdaptor * adaptor,
227                               GObject * object, GObject * child)
228 {
229   gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child));
230 
231   glade_gtk_table_refresh_placeholders (GTK_TABLE (object));
232 }
233 
234 void
glade_gtk_table_replace_child(GladeWidgetAdaptor * adaptor,GtkWidget * container,GtkWidget * current,GtkWidget * new_widget)235 glade_gtk_table_replace_child (GladeWidgetAdaptor * adaptor,
236                                GtkWidget * container,
237                                GtkWidget * current, GtkWidget * new_widget)
238 {
239   /* Chain Up */
240   GWA_GET_CLASS
241       (GTK_TYPE_CONTAINER)->replace_child (adaptor,
242                                            G_OBJECT (container),
243                                            G_OBJECT (current),
244                                            G_OBJECT (new_widget));
245 
246   /* If we are replacing a GladeWidget, we must refresh placeholders
247    * because the widget may have spanned multiple rows/columns, we must
248    * not do so in the case we are pasting multiple widgets into a table,
249    * where destroying placeholders results in default packing properties
250    * (since the remaining placeholder templates no longer exist, only the
251    * first pasted widget would have proper packing properties).
252    */
253   if (!GLADE_IS_PLACEHOLDER (new_widget))
254     glade_gtk_table_refresh_placeholders (GTK_TABLE (container));
255 
256 }
257 
258 static void
glade_gtk_table_set_n_common(GObject * object,const GValue * value,gboolean for_rows)259 glade_gtk_table_set_n_common (GObject * object, const GValue * value,
260                               gboolean for_rows)
261 {
262   GladeWidget *widget;
263   GtkTable *table;
264   guint new_size, old_size, n_columns, n_rows;
265 
266   table = GTK_TABLE (object);
267 
268   g_object_get (table, "n-columns", &n_columns, "n-rows", &n_rows, NULL);
269 
270   new_size = g_value_get_uint (value);
271   old_size = for_rows ? n_rows : n_columns;
272 
273   if (new_size < 1)
274     return;
275 
276   if (glade_gtk_table_widget_exceeds_bounds
277       (table, for_rows ? new_size : n_rows, for_rows ? n_columns : new_size))
278     /* Refuse to shrink if it means orphaning widgets */
279     return;
280 
281   widget = glade_widget_get_from_gobject (GTK_WIDGET (table));
282   g_return_if_fail (widget != NULL);
283 
284   G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
285   if (for_rows)
286     gtk_table_resize (table, new_size, n_columns);
287   else
288     gtk_table_resize (table, n_rows, new_size);
289   G_GNUC_END_IGNORE_DEPRECATIONS;
290 
291   /* Fill table with placeholders */
292   glade_gtk_table_refresh_placeholders (table);
293 
294   if (new_size < old_size)
295     {
296       /* Remove from the bottom up */
297       GList *list, *children;
298       GList *list_to_free = NULL;
299 
300       children = gtk_container_get_children (GTK_CONTAINER (table));
301 
302       for (list = children; list && list->data; list = list->next)
303         {
304           GtkTableChild child;
305           guint start, end;
306 
307           glade_gtk_table_get_child_attachments (GTK_WIDGET (table),
308                                                  GTK_WIDGET (list->data),
309                                                  &child);
310 
311           start = for_rows ? child.top_attach : child.left_attach;
312           end = for_rows ? child.bottom_attach : child.right_attach;
313 
314           /* We need to completely remove it */
315           if (start >= new_size)
316             {
317               list_to_free = g_list_prepend (list_to_free, child.widget);
318               continue;
319             }
320 
321           /* If the widget spans beyond the new border,
322            * we should resize it to fit on the new table */
323           if (end > new_size)
324             gtk_container_child_set
325                 (GTK_CONTAINER (table), GTK_WIDGET (child.widget),
326                  for_rows ? "bottom_attach" : "right_attach", new_size, NULL);
327         }
328 
329       g_list_free (children);
330 
331       if (list_to_free)
332         {
333           for (list = g_list_first (list_to_free);
334                list && list->data; list = list->next)
335             {
336               g_object_ref (G_OBJECT (list->data));
337               gtk_container_remove (GTK_CONTAINER (table),
338                                     GTK_WIDGET (list->data));
339               /* This placeholder is no longer valid, force destroy */
340               gtk_widget_destroy (GTK_WIDGET (list->data));
341             }
342           g_list_free (list_to_free);
343         }
344 
345       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
346       gtk_table_resize (table,
347                         for_rows ? new_size : n_rows,
348                         for_rows ? n_columns : new_size);
349       G_GNUC_END_IGNORE_DEPRECATIONS;
350     }
351 }
352 
353 void
glade_gtk_table_set_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,const GValue * value)354 glade_gtk_table_set_property (GladeWidgetAdaptor * adaptor,
355                               GObject * object,
356                               const gchar * id, const GValue * value)
357 {
358   if (!strcmp (id, "n-rows"))
359     glade_gtk_table_set_n_common (object, value, TRUE);
360   else if (!strcmp (id, "n-columns"))
361     glade_gtk_table_set_n_common (object, value, FALSE);
362   else
363     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object,
364                                                       id, value);
365 }
366 
367 static gboolean
glade_gtk_table_verify_n_common(GObject * object,const GValue * value,gboolean for_rows)368 glade_gtk_table_verify_n_common (GObject * object, const GValue * value,
369                                  gboolean for_rows)
370 {
371   GtkTable *table = GTK_TABLE (object);
372   guint n_columns, n_rows, new_size = g_value_get_uint (value);
373 
374   g_object_get (table, "n-columns", &n_columns, "n-rows", &n_rows, NULL);
375 
376   if (glade_gtk_table_widget_exceeds_bounds
377       (table, for_rows ? new_size : n_rows, for_rows ? n_columns : new_size))
378     /* Refuse to shrink if it means orphaning widgets */
379     return FALSE;
380 
381   return TRUE;
382 }
383 
384 gboolean
glade_gtk_table_verify_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,const GValue * value)385 glade_gtk_table_verify_property (GladeWidgetAdaptor * adaptor,
386                                  GObject * object,
387                                  const gchar * id, const GValue * value)
388 {
389   if (!strcmp (id, "n-rows"))
390     return glade_gtk_table_verify_n_common (object, value, TRUE);
391   else if (!strcmp (id, "n-columns"))
392     return glade_gtk_table_verify_n_common (object, value, FALSE);
393   else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property)
394     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property (adaptor, object,
395                                                          id, value);
396 
397   return TRUE;
398 }
399 
400 void
glade_gtk_table_set_child_property(GladeWidgetAdaptor * adaptor,GObject * container,GObject * child,const gchar * property_name,GValue * value)401 glade_gtk_table_set_child_property (GladeWidgetAdaptor * adaptor,
402                                     GObject * container,
403                                     GObject * child,
404                                     const gchar * property_name, GValue * value)
405 {
406   GWA_GET_CLASS
407       (GTK_TYPE_CONTAINER)->child_set_property (adaptor,
408                                                 container, child,
409                                                 property_name, value);
410 
411   if (strcmp (property_name, "bottom-attach") == 0 ||
412       strcmp (property_name, "left-attach") == 0 ||
413       strcmp (property_name, "right-attach") == 0 ||
414       strcmp (property_name, "top-attach") == 0)
415     {
416       /* Refresh placeholders */
417       glade_gtk_table_refresh_placeholders (GTK_TABLE (container));
418     }
419 
420 }
421 
422 static gboolean
glade_gtk_table_verify_attach_common(GObject * object,GValue * value,guint * val,const gchar * prop,guint * prop_val,const gchar * parent_prop,guint * parent_val)423 glade_gtk_table_verify_attach_common (GObject * object,
424                                       GValue * value,
425                                       guint * val,
426                                       const gchar * prop,
427                                       guint * prop_val,
428                                       const gchar * parent_prop,
429                                       guint * parent_val)
430 {
431   GladeWidget *widget, *parent;
432 
433   widget = glade_widget_get_from_gobject (object);
434   g_return_val_if_fail (GLADE_IS_WIDGET (widget), TRUE);
435   parent = glade_widget_get_parent (widget);
436   g_return_val_if_fail (GLADE_IS_WIDGET (parent), TRUE);
437 
438   *val = g_value_get_uint (value);
439   glade_widget_property_get (widget, prop, prop_val);
440   glade_widget_property_get (parent, parent_prop, parent_val);
441 
442   return FALSE;
443 }
444 
445 static gboolean
glade_gtk_table_verify_left_top_attach(GObject * object,GValue * value,const gchar * prop,const gchar * parent_prop)446 glade_gtk_table_verify_left_top_attach (GObject * object,
447                                         GValue * value,
448                                         const gchar * prop,
449                                         const gchar * parent_prop)
450 {
451   guint val, prop_val, parent_val;
452 
453   if (glade_gtk_table_verify_attach_common (object, value, &val,
454                                             prop, &prop_val,
455                                             parent_prop, &parent_val))
456     return FALSE;
457 
458   if (val >= parent_val || val >= prop_val)
459     return FALSE;
460 
461   return TRUE;
462 }
463 
464 static gboolean
glade_gtk_table_verify_right_bottom_attach(GObject * object,GValue * value,const gchar * prop,const gchar * parent_prop)465 glade_gtk_table_verify_right_bottom_attach (GObject * object,
466                                             GValue * value,
467                                             const gchar * prop,
468                                             const gchar * parent_prop)
469 {
470   guint val, prop_val, parent_val;
471 
472   if (glade_gtk_table_verify_attach_common (object, value, &val,
473                                             prop, &prop_val,
474                                             parent_prop, &parent_val))
475     return FALSE;
476 
477   if (val <= prop_val || val > parent_val)
478     return FALSE;
479 
480   return TRUE;
481 }
482 
483 gboolean
glade_gtk_table_child_verify_property(GladeWidgetAdaptor * adaptor,GObject * container,GObject * child,const gchar * id,GValue * value)484 glade_gtk_table_child_verify_property (GladeWidgetAdaptor * adaptor,
485                                        GObject * container,
486                                        GObject * child,
487                                        const gchar * id, GValue * value)
488 {
489   if (!strcmp (id, "left-attach"))
490     return glade_gtk_table_verify_left_top_attach (child,
491                                                    value,
492                                                    "right-attach", "n-columns");
493   else if (!strcmp (id, "right-attach"))
494     return glade_gtk_table_verify_right_bottom_attach (child,
495                                                        value,
496                                                        "left-attach",
497                                                        "n-columns");
498   else if (!strcmp (id, "top-attach"))
499     return glade_gtk_table_verify_left_top_attach (child,
500                                                    value,
501                                                    "bottom-attach", "n-rows");
502   else if (!strcmp (id, "bottom-attach"))
503     return glade_gtk_table_verify_right_bottom_attach (child,
504                                                        value,
505                                                        "top-attach", "n-rows");
506   else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_verify_property)
507     GWA_GET_CLASS
508         (GTK_TYPE_CONTAINER)->child_verify_property (adaptor,
509                                                      container, child,
510                                                      id, value);
511 
512   return TRUE;
513 }
514 
515 static void
glade_gtk_table_child_insert_remove_action(GladeWidgetAdaptor * adaptor,GObject * container,GObject * object,GroupAction group_action,const gchar * n_row_col,const gchar * attach1,const gchar * attach2,gboolean remove,gboolean after)516 glade_gtk_table_child_insert_remove_action (GladeWidgetAdaptor *adaptor,
517 					    GObject            *container,
518 					    GObject            *object,
519 					    GroupAction         group_action,
520 					    const gchar        *n_row_col,
521 					    const gchar        *attach1,    /* should be smaller (top/left) attachment */
522                                             const gchar        *attach2,      /* should be larger (bot/right) attachment */
523                                             gboolean            remove,
524 					    gboolean            after)
525 {
526   GladeWidget *parent;
527   GList *children, *l;
528   gint child_pos, size, offset;
529 
530   gtk_container_child_get (GTK_CONTAINER (container),
531                            GTK_WIDGET (object),
532                            after ? attach2 : attach1, &child_pos, NULL);
533 
534   parent = glade_widget_get_from_gobject (container);
535   switch (group_action)
536     {
537       case GROUP_ACTION_INSERT_ROW:
538         glade_command_push_group (_("Insert Row on %s"), glade_widget_get_name (parent));
539         break;
540       case GROUP_ACTION_INSERT_COLUMN:
541         glade_command_push_group (_("Insert Column on %s"), glade_widget_get_name (parent));
542         break;
543       case GROUP_ACTION_REMOVE_COLUMN:
544         glade_command_push_group (_("Remove Column on %s"), glade_widget_get_name (parent));
545         break;
546       case GROUP_ACTION_REMOVE_ROW:
547         glade_command_push_group (_("Remove Row on %s"), glade_widget_get_name (parent));
548         break;
549       default:
550         g_assert_not_reached ();
551     }
552 
553   children = glade_widget_adaptor_get_children (adaptor, container);
554   /* Make sure widgets does not get destroyed */
555   g_list_foreach (children, (GFunc) g_object_ref, NULL);
556 
557   glade_widget_property_get (parent, n_row_col, &size);
558 
559   if (remove)
560     {
561       GList *del = NULL;
562       /* Remove children first */
563       for (l = children; l; l = g_list_next (l))
564         {
565           GladeWidget *gchild = glade_widget_get_from_gobject (l->data);
566           gint pos1, pos2;
567 
568           /* Skip placeholders */
569           if (gchild == NULL)
570             continue;
571 
572           glade_widget_pack_property_get (gchild, attach1, &pos1);
573           glade_widget_pack_property_get (gchild, attach2, &pos2);
574           if ((pos1 + 1 == pos2) && ((after ? pos2 : pos1) == child_pos))
575             {
576               del = g_list_prepend (del, gchild);
577             }
578         }
579       if (del)
580         {
581           glade_command_delete (del);
582           g_list_free (del);
583         }
584       offset = -1;
585     }
586   else
587     {
588       /* Expand the table */
589       glade_command_set_property (glade_widget_get_property (parent, n_row_col),
590                                   size + 1);
591       offset = 1;
592     }
593 
594   /* Reorder children */
595   for (l = children; l; l = g_list_next (l))
596     {
597       GladeWidget *gchild = glade_widget_get_from_gobject (l->data);
598       gint pos;
599 
600       /* Skip placeholders */
601       if (gchild == NULL)
602         continue;
603 
604       /* if removing, do top/left before bot/right */
605       if (remove)
606         {
607           /* adjust top-left attachment */
608           glade_widget_pack_property_get (gchild, attach1, &pos);
609           if (pos > child_pos || (after && pos == child_pos))
610             {
611               glade_command_set_property (glade_widget_get_pack_property
612                                           (gchild, attach1), pos + offset);
613             }
614 
615           /* adjust bottom-right attachment */
616           glade_widget_pack_property_get (gchild, attach2, &pos);
617           if (pos > child_pos || (after && pos == child_pos))
618             {
619               glade_command_set_property (glade_widget_get_pack_property
620                                           (gchild, attach2), pos + offset);
621             }
622 
623         }
624       /* if inserting, do bot/right before top/left */
625       else
626         {
627           /* adjust bottom-right attachment */
628           glade_widget_pack_property_get (gchild, attach2, &pos);
629           if (pos > child_pos)
630             {
631               glade_command_set_property (glade_widget_get_pack_property
632                                           (gchild, attach2), pos + offset);
633             }
634 
635           /* adjust top-left attachment */
636           glade_widget_pack_property_get (gchild, attach1, &pos);
637           if (pos >= child_pos)
638             {
639               glade_command_set_property (glade_widget_get_pack_property
640                                           (gchild, attach1), pos + offset);
641             }
642         }
643     }
644 
645   if (remove)
646     {
647       /* Shrink the table */
648       glade_command_set_property (glade_widget_get_property (parent, n_row_col),
649                                   size - 1);
650     }
651 
652   g_list_foreach (children, (GFunc) g_object_unref, NULL);
653   g_list_free (children);
654 
655   glade_command_pop_group ();
656 }
657 
658 void
glade_gtk_table_child_action_activate(GladeWidgetAdaptor * adaptor,GObject * container,GObject * object,const gchar * action_path)659 glade_gtk_table_child_action_activate (GladeWidgetAdaptor * adaptor,
660                                        GObject * container,
661                                        GObject * object,
662                                        const gchar * action_path)
663 {
664   if (strcmp (action_path, "insert_row/after") == 0)
665     {
666       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
667                                                   GROUP_ACTION_INSERT_ROW,
668                                                   "n-rows", "top-attach",
669                                                   "bottom-attach", FALSE, TRUE);
670     }
671   else if (strcmp (action_path, "insert_row/before") == 0)
672     {
673       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
674                                                   GROUP_ACTION_INSERT_ROW,
675                                                   "n-rows", "top-attach",
676                                                   "bottom-attach",
677                                                   FALSE, FALSE);
678     }
679   else if (strcmp (action_path, "insert_column/after") == 0)
680     {
681       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
682                                                   GROUP_ACTION_INSERT_COLUMN,
683                                                   "n-columns", "left-attach",
684                                                   "right-attach", FALSE, TRUE);
685     }
686   else if (strcmp (action_path, "insert_column/before") == 0)
687     {
688       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
689                                                   GROUP_ACTION_INSERT_COLUMN,
690                                                   "n-columns", "left-attach",
691                                                   "right-attach", FALSE, FALSE);
692     }
693   else if (strcmp (action_path, "remove_column") == 0)
694     {
695       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
696                                                   GROUP_ACTION_REMOVE_COLUMN,
697                                                   "n-columns", "left-attach",
698                                                   "right-attach", TRUE, FALSE);
699     }
700   else if (strcmp (action_path, "remove_row") == 0)
701     {
702       glade_gtk_table_child_insert_remove_action (adaptor, container, object,
703                                                   GROUP_ACTION_REMOVE_ROW,
704                                                   "n-rows", "top-attach",
705                                                   "bottom-attach", TRUE, FALSE);
706     }
707   else
708     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor,
709                                                                container,
710                                                                object,
711                                                                action_path);
712 }
713