1 
2 /*
3  * Copyright (C) 2006-2016 Juan Pablo Ugarte.
4  *
5  * This library is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * Authors:
20  *   Juan Pablo Ugarte <juanpablougarte@gmail.com>
21  */
22 
23 #include "config.h"
24 
25 /**
26  * SECTION:glade-base-editor
27  * @Short_Description: A customisable editor
28  *
29  * Convenience object to edit containers where placeholders do not make sense, like GtkMenubar.
30  */
31 
32 #include "glade.h"
33 #include "glade-marshallers.h"
34 #include "glade-editor-property.h"
35 #include "glade-base-editor.h"
36 #include "glade-app.h"
37 #include "glade-popup.h"
38 #include "glade-accumulators.h"
39 
40 #include <string.h>
41 #include <glib/gi18n-lib.h>
42 #include <gdk/gdkkeysyms.h>
43 
44 typedef enum
45 {
46   GLADE_BASE_EDITOR_GTYPE,
47   GLADE_BASE_EDITOR_CLASS_NAME,
48   GLADE_BASE_EDITOR_TYPES_N_COLUMNS
49 } GladeBaseEditorChildEnum;
50 
51 typedef enum
52 {
53   GLADE_BASE_EDITOR_GWIDGET,
54   GLADE_BASE_EDITOR_OBJECT,
55   GLADE_BASE_EDITOR_TYPE_NAME,
56   GLADE_BASE_EDITOR_NAME,
57   GLADE_BASE_EDITOR_CHILD_TYPES,
58   GLADE_BASE_EDITOR_N_COLUMNS
59 } GladeBaseEditorEnum;
60 
61 typedef enum {
62   ADD_ROOT = 0,
63   ADD_SIBLING,
64   ADD_CHILD
65 } GladeBaseEditorAddMode;
66 
67 typedef struct
68 {
69   GType parent_type;
70   GtkTreeModel *children;
71 } ChildTypeTab;
72 
73 struct _GladeBaseEditorPrivate
74 {
75   GladeWidget *gcontainer;      /* The container we are editing */
76 
77   /* Editor UI */
78   GtkWidget *paned, *table, *treeview, *tip_label;
79   GtkWidget *add_button, *delete_button, *help_button;
80   GladeSignalEditor *signal_editor;
81 
82   GList *child_types;
83 
84   GtkTreeModel *model;
85   GladeProject *project;
86 
87   /* Add button data */
88   GType add_type;
89 
90   /* Temporal variables */
91   GtkTreeIter iter;             /* used in idle functions */
92   gint row;
93 
94   gboolean updating_treeview;
95 
96   guint properties_idle;
97 };
98 
99 enum
100 {
101   SIGNAL_CHILD_SELECTED,
102   SIGNAL_CHANGE_TYPE,
103   SIGNAL_GET_DISPLAY_NAME,
104   SIGNAL_BUILD_CHILD,
105   SIGNAL_DELETE_CHILD,
106   SIGNAL_MOVE_CHILD,
107   LAST_SIGNAL
108 };
109 
110 enum
111 {
112   PROP_0,
113   PROP_CONTAINER,
114   N_PROPERTIES
115 };
116 
G_DEFINE_TYPE_WITH_PRIVATE(GladeBaseEditor,glade_base_editor,GTK_TYPE_BOX)117 G_DEFINE_TYPE_WITH_PRIVATE (GladeBaseEditor, glade_base_editor, GTK_TYPE_BOX)
118 
119 static GParamSpec *properties[N_PROPERTIES];
120 static guint glade_base_editor_signals[LAST_SIGNAL] = { 0 };
121 
122 static void glade_base_editor_set_container (GladeBaseEditor *editor,
123                                              GObject         *container);
124 static void glade_base_editor_block_callbacks (GladeBaseEditor *editor,
125                                                gboolean         block);
126 
127 
128 static void
reset_child_types(GladeBaseEditor * editor)129 reset_child_types (GladeBaseEditor *editor)
130 {
131   GList *l;
132   ChildTypeTab *tab;
133 
134   for (l = editor->priv->child_types; l; l = l->next)
135     {
136       tab = l->data;
137       g_object_unref (tab->children);
138       g_free (tab);
139     }
140   g_list_free (editor->priv->child_types);
141   editor->priv->child_types = NULL;
142 }
143 
144 
145 static gint
sort_type_by_hierarchy(ChildTypeTab * a,ChildTypeTab * b)146 sort_type_by_hierarchy (ChildTypeTab *a, ChildTypeTab *b)
147 {
148   if (g_type_is_a (a->parent_type, b->parent_type))
149     return -1;
150 
151   return 1;
152 }
153 
154 static GtkTreeModel *
get_children_model_for_type(GladeBaseEditor * editor,GType type)155 get_children_model_for_type (GladeBaseEditor *editor, GType type)
156 {
157   GList *l;
158   for (l = editor->priv->child_types; l; l = l->next)
159     {
160       ChildTypeTab *tab = l->data;
161       if (g_type_is_a (type, tab->parent_type))
162         return tab->children;
163     }
164   return NULL;
165 }
166 
167 static GtkTreeModel *
get_children_model_for_child_type(GladeBaseEditor * editor,GType type)168 get_children_model_for_child_type (GladeBaseEditor *editor, GType type)
169 {
170   GList *l;
171   GtkTreeModel *model = NULL;
172 
173   /* Get deep derived classes first and work up the sorted heirarchy */
174   for (l = g_list_last (editor->priv->child_types); model == NULL && l;
175        l = l->prev)
176     {
177       ChildTypeTab *tab = l->data;
178       GtkTreeIter iter;
179       GType iter_type;
180 
181       if (!gtk_tree_model_get_iter_first (tab->children, &iter))
182         continue;
183 
184       do
185         {
186           gtk_tree_model_get (tab->children, &iter,
187                               GLADE_BASE_EDITOR_GTYPE, &iter_type, -1);
188 
189           /* Find exact match types in this case */
190           if (iter_type == type)
191             model = tab->children;
192 
193         }
194       while (model == NULL && gtk_tree_model_iter_next (tab->children, &iter));
195     }
196 
197   return model;
198 }
199 
200 static gboolean
glade_base_editor_get_type_info(GladeBaseEditor * e,GtkTreeIter * retiter,GType child_type,...)201 glade_base_editor_get_type_info (GladeBaseEditor *e,
202                                  GtkTreeIter     *retiter,
203                                  GType            child_type,
204                                  ...)
205 {
206   GtkTreeModel *model;
207   GtkTreeIter iter;
208   GType type;
209 
210   model = get_children_model_for_child_type (e, child_type);
211 
212   if (!model || gtk_tree_model_get_iter_first (model, &iter) == FALSE)
213     return FALSE;
214 
215   do
216     {
217       gtk_tree_model_get (model, &iter, GLADE_BASE_EDITOR_GTYPE, &type, -1);
218 
219       if (child_type == type)
220         {
221           va_list args;
222           va_start (args, child_type);
223           gtk_tree_model_get_valist (model, &iter, args);
224           va_end (args);
225           if (retiter)
226             *retiter = iter;
227           return TRUE;
228         }
229     }
230   while (gtk_tree_model_iter_next (model, &iter));
231 
232   return FALSE;
233 }
234 
235 static gchar *
glade_base_editor_get_display_name(GladeBaseEditor * editor,GladeWidget * gchild)236 glade_base_editor_get_display_name (GladeBaseEditor *editor,
237                                     GladeWidget     *gchild)
238 {
239   gchar *retval;
240   g_signal_emit (editor,
241                  glade_base_editor_signals[SIGNAL_GET_DISPLAY_NAME],
242                  0, gchild, &retval);
243 
244   return retval;
245 }
246 
247 static void
glade_base_editor_fill_store_real(GladeBaseEditor * e,GladeWidget * gwidget,GtkTreeIter * parent)248 glade_base_editor_fill_store_real (GladeBaseEditor *e,
249                                    GladeWidget     *gwidget,
250                                    GtkTreeIter     *parent)
251 {
252   GList *children, *l;
253   GtkTreeIter iter;
254 
255   children = glade_widget_get_children (gwidget);
256 
257   for (l = children; l; l = l->next)
258     {
259       GladeWidget *gchild;
260       GObject     *child = l->data;
261       gchar       *type_name = NULL, *name;
262 
263       gchild = glade_widget_get_from_gobject (child);
264 
265       /* Have to check parents here for compatibility (could be the parenting menuitem of this menu
266        * supports a menuitem...) */
267       if (glade_base_editor_get_type_info (e, NULL,
268                                            G_OBJECT_TYPE (child),
269                                            GLADE_BASE_EDITOR_CLASS_NAME,
270                                            &type_name, -1))
271         {
272           gtk_tree_store_append (GTK_TREE_STORE (e->priv->model), &iter, parent);
273 
274           name = glade_base_editor_get_display_name (e, gchild);
275 
276           gtk_tree_store_set (GTK_TREE_STORE (e->priv->model), &iter,
277                               GLADE_BASE_EDITOR_GWIDGET, gchild,
278                               GLADE_BASE_EDITOR_OBJECT, child,
279                               GLADE_BASE_EDITOR_TYPE_NAME, type_name,
280                               GLADE_BASE_EDITOR_NAME, name,
281                               GLADE_BASE_EDITOR_CHILD_TYPES,
282                               get_children_model_for_child_type (e, G_OBJECT_TYPE (child)),
283                               -1);
284 
285           glade_base_editor_fill_store_real (e, gchild, &iter);
286 
287           g_free (name);
288           g_free (type_name);
289       }
290       else
291         glade_base_editor_fill_store_real (e, gchild, parent);
292     }
293 
294   g_list_free (children);
295 }
296 
297 static void
glade_base_editor_fill_store(GladeBaseEditor * e)298 glade_base_editor_fill_store (GladeBaseEditor *e)
299 {
300   gtk_tree_store_clear (GTK_TREE_STORE (e->priv->model));
301   gtk_tree_view_set_model (GTK_TREE_VIEW (e->priv->treeview), NULL);
302   glade_base_editor_fill_store_real (e, e->priv->gcontainer, NULL);
303   gtk_tree_view_set_model (GTK_TREE_VIEW (e->priv->treeview), e->priv->model);
304 
305   gtk_tree_view_expand_all (GTK_TREE_VIEW (e->priv->treeview));
306 
307 }
308 
309 static gboolean
glade_base_editor_get_child_selected(GladeBaseEditor * e,GtkTreeIter * iter)310 glade_base_editor_get_child_selected (GladeBaseEditor *e, GtkTreeIter *iter)
311 {
312   GtkTreeSelection *sel =
313     gtk_tree_view_get_selection (GTK_TREE_VIEW (e->priv->treeview));
314   return (sel) ? gtk_tree_selection_get_selected (sel, NULL, iter) : FALSE;
315 }
316 
317 /* Forward declaration for glade_base_editor_project_widget_name_changed */
318 static void
319 glade_base_editor_project_widget_name_changed (GladeProject *project,
320                                                GladeWidget *widget,
321                                                GladeBaseEditor *editor);
322 
323 
324 static GladeWidget *
glade_base_editor_delegate_build_child(GladeBaseEditor * editor,GladeWidget * parent,GType type)325 glade_base_editor_delegate_build_child (GladeBaseEditor *editor,
326                                         GladeWidget     *parent,
327                                         GType            type)
328 {
329   GladeWidget *child = NULL;
330   g_signal_emit (editor, glade_base_editor_signals[SIGNAL_BUILD_CHILD],
331                  0, parent, type, &child);
332   return child;
333 }
334 
335 static gboolean
glade_base_editor_delegate_delete_child(GladeBaseEditor * editor,GladeWidget * parent,GladeWidget * child)336 glade_base_editor_delegate_delete_child (GladeBaseEditor *editor,
337                                          GladeWidget     *parent,
338                                          GladeWidget     *child)
339 {
340   gboolean retval;
341 
342   g_signal_emit (editor, glade_base_editor_signals[SIGNAL_DELETE_CHILD],
343                  0, parent, child, &retval);
344 
345   return retval;
346 }
347 
348 static void
glade_base_editor_name_activate(GtkEntry * entry,GladeWidget * gchild)349 glade_base_editor_name_activate (GtkEntry *entry, GladeWidget *gchild)
350 {
351   const gchar *text = gtk_entry_get_text (GTK_ENTRY (entry));
352   GladeBaseEditor *editor = g_object_get_data (G_OBJECT (entry), "editor");
353   gchar *new_name = NULL;
354 
355   if (text == NULL || text[0] == '\0')
356     {
357       /* If we are explicitly trying to set the widget name to be empty,
358        * then we must not allow it there are any active references to
359        * the widget which would otherwise break.
360        */
361       if (!glade_widget_has_prop_refs (gchild))
362         new_name = glade_project_new_widget_name (editor->priv->project, NULL, GLADE_UNNAMED_PREFIX);
363     }
364   else
365     new_name = g_strdup (text);
366 
367   if (new_name && new_name[0])
368     {
369       g_signal_handlers_block_by_func (editor->priv->project,
370                                        glade_base_editor_project_widget_name_changed, editor);
371       glade_command_set_name (gchild, new_name);
372       g_signal_handlers_unblock_by_func (editor->priv->project,
373                                          glade_base_editor_project_widget_name_changed, editor);
374     }
375 
376   g_free (new_name);
377 }
378 
379 static void
glade_base_editor_table_attach(GladeBaseEditor * e,GtkWidget * child1,GtkWidget * child2)380 glade_base_editor_table_attach (GladeBaseEditor *e,
381                                 GtkWidget *child1,
382                                 GtkWidget *child2)
383 {
384   GtkGrid *table = GTK_GRID (e->priv->table);
385   gint row = e->priv->row;
386 
387   if (child1)
388     {
389       gtk_grid_attach (table, child1, 0, row, 1, 1);
390       gtk_widget_set_hexpand (child1, TRUE);
391       gtk_widget_show (child1);
392     }
393 
394   if (child2)
395     {
396       gtk_grid_attach (table, child2, 1, row, 1, 1);
397       gtk_widget_show (child2);
398     }
399 
400   e->priv->row++;
401 }
402 
403 static void
glade_base_editor_clear(GladeBaseEditor * editor)404 glade_base_editor_clear (GladeBaseEditor *editor)
405 {
406   GladeBaseEditorPrivate *e = editor->priv;
407 
408   gtk_widget_show (e->tip_label);
409   gtk_container_foreach (GTK_CONTAINER (e->table),
410                          (GtkCallback)gtk_widget_destroy, NULL);
411   e->row = 0;
412   gtk_widget_set_sensitive (e->delete_button, FALSE);
413   glade_signal_editor_load_widget (e->signal_editor, NULL);
414 }
415 
416 static void
glade_base_editor_treeview_cursor_changed(GtkTreeView * treeview,GladeBaseEditor * editor)417 glade_base_editor_treeview_cursor_changed (GtkTreeView     *treeview,
418                                            GladeBaseEditor *editor)
419 {
420   GladeBaseEditorPrivate *e = editor->priv;
421   GtkTreeIter iter;
422   GObject *child;
423   GladeWidget *gchild;
424 
425   g_return_if_fail (GTK_IS_TREE_VIEW (treeview));
426 
427   if (!glade_base_editor_get_child_selected (editor, &iter))
428     return;
429 
430   glade_base_editor_clear (editor);
431   gtk_widget_set_sensitive (e->delete_button, TRUE);
432 
433   gtk_tree_model_get (e->model, &iter,
434                       GLADE_BASE_EDITOR_GWIDGET, &gchild,
435                       GLADE_BASE_EDITOR_OBJECT, &child, -1);
436 
437   g_object_unref (gchild);
438   g_object_unref (child);
439 
440   /* Emit child-selected signal and let the user add the properties */
441   g_signal_emit (editor, glade_base_editor_signals[SIGNAL_CHILD_SELECTED],
442                  0, gchild);
443 
444   /* Update Signal Editor */
445   glade_signal_editor_load_widget (e->signal_editor, gchild);
446 }
447 
448 static gboolean
glade_base_editor_update_properties_idle(gpointer data)449 glade_base_editor_update_properties_idle (gpointer data)
450 {
451   GladeBaseEditor *editor = (GladeBaseEditor *) data;
452   glade_base_editor_treeview_cursor_changed
453       (GTK_TREE_VIEW (editor->priv->treeview), editor);
454   editor->priv->properties_idle = 0;
455   return FALSE;
456 }
457 
458 /* XXX Can we make it crisper by removing this idle ?? */
459 static void
glade_base_editor_update_properties(GladeBaseEditor * editor)460 glade_base_editor_update_properties (GladeBaseEditor *editor)
461 {
462   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
463 
464   if (!editor->priv->properties_idle)
465     editor->priv->properties_idle =
466         g_idle_add (glade_base_editor_update_properties_idle, editor);
467 }
468 
469 static void
glade_base_editor_set_cursor(GladeBaseEditor * e,GtkTreeIter * iter)470 glade_base_editor_set_cursor (GladeBaseEditor *e, GtkTreeIter *iter)
471 {
472   GtkTreePath *path;
473   GtkTreeIter real_iter;
474 
475   if (iter == NULL && glade_base_editor_get_child_selected (e, &real_iter))
476     iter = &real_iter;
477 
478   if (iter && (path = gtk_tree_model_get_path (e->priv->model, iter)))
479     {
480       gtk_tree_view_set_cursor (GTK_TREE_VIEW (e->priv->treeview), path, NULL,
481                                 FALSE);
482       gtk_tree_path_free (path);
483     }
484 }
485 
486 static gboolean
glade_base_editor_find_child_real(GladeBaseEditor * e,GladeWidget * gchild,GtkTreeIter * iter)487 glade_base_editor_find_child_real (GladeBaseEditor *e,
488                                    GladeWidget     *gchild,
489                                    GtkTreeIter     *iter)
490 {
491   GtkTreeModel *model = e->priv->model;
492   GtkTreeIter child_iter;
493   GladeWidget *child;
494 
495   do
496     {
497       gtk_tree_model_get (model, iter, GLADE_BASE_EDITOR_GWIDGET, &child, -1);
498       g_object_unref (child);
499 
500       if (child == gchild)
501         return TRUE;
502 
503       if (gtk_tree_model_iter_children (model, &child_iter, iter))
504         if (glade_base_editor_find_child_real (e, gchild, &child_iter))
505           {
506             *iter = child_iter;
507             return TRUE;
508           }
509     }
510   while (gtk_tree_model_iter_next (model, iter));
511 
512   return FALSE;
513 }
514 
515 static gboolean
glade_base_editor_find_child(GladeBaseEditor * e,GladeWidget * child,GtkTreeIter * iter)516 glade_base_editor_find_child (GladeBaseEditor *e,
517                               GladeWidget     *child,
518                               GtkTreeIter     *iter)
519 {
520   if (gtk_tree_model_get_iter_first (e->priv->model, iter))
521     return glade_base_editor_find_child_real (e, child, iter);
522 
523   return FALSE;
524 }
525 
526 static void
glade_base_editor_select_child(GladeBaseEditor * e,GladeWidget * child)527 glade_base_editor_select_child (GladeBaseEditor *e, GladeWidget *child)
528 {
529   GtkTreeIter iter;
530 
531   if (glade_base_editor_find_child (e, child, &iter))
532     glade_base_editor_set_cursor (e, &iter);
533 }
534 
535 static void
glade_base_editor_child_change_type(GladeBaseEditor * editor,GtkTreeIter * iter,GType type)536 glade_base_editor_child_change_type (GladeBaseEditor *editor,
537                                      GtkTreeIter     *iter,
538                                      GType            type)
539 {
540   GladeWidget *gchild;
541   GObject *child;
542   gchar *class_name;
543   gboolean retval;
544 
545   glade_base_editor_block_callbacks (editor, TRUE);
546 
547   /* Get old widget data */
548   gtk_tree_model_get (editor->priv->model, iter,
549                       GLADE_BASE_EDITOR_GWIDGET, &gchild,
550                       GLADE_BASE_EDITOR_OBJECT, &child, -1);
551 
552   g_object_unref (gchild);
553   g_object_unref (child);
554 
555   if (type == G_OBJECT_TYPE (child) ||
556       !gchild || !glade_widget_get_parent (gchild))
557     {
558       glade_base_editor_block_callbacks (editor, FALSE);
559       return;
560     }
561 
562   /* Start of glade-command */
563   if (glade_base_editor_get_type_info (editor, NULL,
564                                        type,
565                                        GLADE_BASE_EDITOR_CLASS_NAME,
566                                        &class_name, -1))
567     {
568       glade_command_push_group (_("Setting object type on %s to %s"),
569                                 glade_widget_get_name (gchild), class_name);
570       g_free (class_name);
571     }
572   else
573     {
574       glade_base_editor_block_callbacks (editor, FALSE);
575       return;
576     }
577 
578   g_signal_emit (editor,
579                  glade_base_editor_signals[SIGNAL_CHANGE_TYPE],
580                  0, gchild, type, &retval);
581 
582   /* End of glade-command */
583   glade_command_pop_group ();
584 
585   /* Update properties */
586   glade_base_editor_update_properties (editor);
587 
588   glade_base_editor_block_callbacks (editor, FALSE);
589 }
590 
591 static void
glade_base_editor_type_changed(GtkComboBox * widget,GladeBaseEditor * e)592 glade_base_editor_type_changed (GtkComboBox *widget, GladeBaseEditor *e)
593 {
594   GtkTreeIter iter, combo_iter;
595   GType type;
596 
597   if (!glade_base_editor_get_child_selected (e, &iter))
598     return;
599 
600   gtk_combo_box_get_active_iter (widget, &combo_iter);
601 
602   gtk_tree_model_get (gtk_combo_box_get_model (widget), &combo_iter,
603                       GLADE_BASE_EDITOR_GTYPE, &type, -1);
604 
605   glade_base_editor_child_change_type (e, &iter, type);
606 }
607 
608 static void
glade_base_editor_child_type_edited(GtkCellRendererText * cell,const gchar * path_string,const gchar * new_text,GladeBaseEditor * editor)609 glade_base_editor_child_type_edited (GtkCellRendererText *cell,
610                                      const gchar         *path_string,
611                                      const gchar         *new_text,
612                                      GladeBaseEditor     *editor)
613 {
614   GladeBaseEditorPrivate *e = editor->priv;
615   GtkTreeModel *child_class;
616   GtkTreePath *path;
617   GtkTreeIter iter, combo_iter;
618   GType type;
619   gchar *type_name = NULL;
620 
621   path = gtk_tree_path_new_from_string (path_string);
622   gtk_tree_model_get_iter (e->model, &iter, path);
623   gtk_tree_path_free (path);
624 
625   gtk_tree_model_get (e->model, &iter,
626                       GLADE_BASE_EDITOR_TYPE_NAME, &type_name,
627                       GLADE_BASE_EDITOR_CHILD_TYPES, &child_class, -1);
628 
629   if (g_strcmp0 (type_name, new_text) == 0)
630     {
631       g_free (type_name);
632       g_object_unref (child_class);
633       return;
634     }
635 
636   /* Lookup GType */
637   if (!gtk_tree_model_get_iter_first (child_class, &combo_iter))
638     {
639       g_free (type_name);
640       g_object_unref (child_class);
641       return;
642     }
643 
644   g_free (type_name);
645 
646   do
647     {
648       gtk_tree_model_get (child_class, &combo_iter,
649                           GLADE_BASE_EDITOR_GTYPE, &type,
650                           GLADE_BASE_EDITOR_CLASS_NAME, &type_name, -1);
651 
652       if (strcmp (type_name, new_text) == 0)
653         {
654           g_free (type_name);
655           break;
656         }
657 
658       g_free (type_name);
659     }
660   while (gtk_tree_model_iter_next (child_class, &combo_iter));
661 
662   glade_base_editor_child_change_type (editor, &iter, type);
663 }
664 
665 static void
glade_base_editor_reorder_children(GladeBaseEditor * editor,GtkTreeIter * child)666 glade_base_editor_reorder_children (GladeBaseEditor *editor,
667                                     GtkTreeIter     *child)
668 {
669   GtkTreeModel *model = editor->priv->model;
670   GladeWidget *gchild;
671   GladeProperty *property;
672   GtkTreeIter parent, iter;
673   gint position = 0;
674 
675   if (gtk_tree_model_iter_parent (model, &parent, child))
676     gtk_tree_model_iter_children (model, &iter, &parent);
677   else
678     gtk_tree_model_get_iter_first (model, &iter);
679 
680   do
681     {
682       gtk_tree_model_get (model, &iter, GLADE_BASE_EDITOR_GWIDGET, &gchild, -1);
683       g_object_unref (gchild);
684 
685       if ((property = glade_widget_get_property (gchild, "position")) != NULL)
686         glade_command_set_property (property, position);
687       position++;
688     }
689   while (gtk_tree_model_iter_next (model, &iter));
690 }
691 
692 static void
glade_base_editor_add_child(GladeBaseEditor * editor,GType type,GladeBaseEditorAddMode add_mode)693 glade_base_editor_add_child (GladeBaseEditor       *editor,
694                              GType                  type,
695                              GladeBaseEditorAddMode add_mode)
696 {
697   GladeBaseEditorPrivate *e = editor->priv;
698   GtkTreeIter iter, new_iter;
699   GladeWidget *gparent, *gchild_new;
700   gchar *name, *class_name;
701   gboolean selected_iter = FALSE;
702 
703   glade_base_editor_block_callbacks (editor, TRUE);
704 
705   gparent = e->gcontainer;
706 
707   if (add_mode != ADD_ROOT &&
708       (selected_iter = glade_base_editor_get_child_selected (editor, &iter)))
709     {
710       if (add_mode == ADD_CHILD)
711         {
712           gtk_tree_model_get (e->model, &iter,
713                               GLADE_BASE_EDITOR_GWIDGET, &gparent, -1);
714           g_object_unref (gparent);
715         }
716       else if (add_mode == ADD_SIBLING &&
717                gtk_tree_model_iter_parent (e->model, &new_iter, &iter))
718         {
719           gtk_tree_model_get (e->model, &new_iter,
720                               GLADE_BASE_EDITOR_GWIDGET, &gparent, -1);
721           g_object_unref (gparent);
722         }
723     }
724 
725   if (!glade_base_editor_get_type_info (editor, NULL, type,
726                                         GLADE_BASE_EDITOR_CLASS_NAME,
727                                         &class_name, -1))
728     return;
729 
730   glade_command_push_group (_("Add a %s to %s"), class_name,
731                             glade_widget_get_name (gparent));
732 
733   /* Build Child */
734   gchild_new = glade_base_editor_delegate_build_child (editor, gparent, type);
735 
736   if (gchild_new == NULL)
737     {
738       glade_command_pop_group ();
739       return;
740     }
741 
742   if (selected_iter)
743     {
744       if (add_mode == ADD_CHILD)
745         gtk_tree_store_append (GTK_TREE_STORE (editor->priv->model), &new_iter,
746                                &iter);
747       else
748         gtk_tree_store_insert_after (GTK_TREE_STORE (editor->priv->model),
749                                      &new_iter, NULL, &iter);
750     }
751   else
752     gtk_tree_store_append (GTK_TREE_STORE (editor->priv->model), &new_iter,
753                            NULL);
754 
755   name = glade_base_editor_get_display_name (editor, gchild_new);
756 
757   gtk_tree_store_set (GTK_TREE_STORE (editor->priv->model), &new_iter,
758                       GLADE_BASE_EDITOR_GWIDGET, gchild_new,
759                       GLADE_BASE_EDITOR_OBJECT,
760                       glade_widget_get_object (gchild_new),
761                       GLADE_BASE_EDITOR_TYPE_NAME, class_name,
762                       GLADE_BASE_EDITOR_NAME, name,
763                       GLADE_BASE_EDITOR_CHILD_TYPES,
764                       get_children_model_for_type (editor,
765                                                    G_OBJECT_TYPE (glade_widget_get_object (gparent))),
766                       -1);
767 
768   glade_base_editor_reorder_children (editor, &new_iter);
769 
770   gtk_tree_view_expand_all (GTK_TREE_VIEW (e->treeview));
771   glade_base_editor_set_cursor (editor, &new_iter);
772 
773   glade_command_pop_group ();
774 
775   glade_base_editor_block_callbacks (editor, FALSE);
776 
777   g_free (name);
778   g_free (class_name);
779 }
780 
781 
782 static void
glade_base_editor_add_item_activate(GtkMenuItem * menuitem,GladeBaseEditor * e)783 glade_base_editor_add_item_activate (GtkMenuItem     *menuitem,
784                                      GladeBaseEditor *e)
785 {
786   GObject *item = G_OBJECT (menuitem);
787   GType type = GPOINTER_TO_SIZE (g_object_get_data (item, "object_type"));
788   GladeBaseEditorAddMode add_mode =
789       GPOINTER_TO_INT (g_object_get_data (item, "object_add_mode"));
790 
791   glade_base_editor_add_child (e, type, add_mode);
792 }
793 
794 static GtkWidget *
glade_base_editor_popup(GladeBaseEditor * editor,GladeWidget * widget)795 glade_base_editor_popup (GladeBaseEditor *editor, GladeWidget *widget)
796 {
797   GtkWidget *popup, *item;
798   GtkTreeModel *model;
799   GtkTreeIter iter;
800   GType iter_type;
801   gchar *label;
802   gchar *class_name;
803 
804   if ((model =
805        get_children_model_for_child_type (editor,
806                                           G_OBJECT_TYPE (glade_widget_get_object (widget)))) == NULL)
807     model =
808       get_children_model_for_type (editor,
809                                    G_OBJECT_TYPE (glade_widget_get_object (editor->priv->gcontainer)));
810 
811   g_assert (model);
812 
813   popup = gtk_menu_new ();
814 
815   if (gtk_tree_model_get_iter_first (model, &iter))
816     do
817       {
818         gtk_tree_model_get (model, &iter,
819                             GLADE_BASE_EDITOR_GTYPE, &iter_type,
820                             GLADE_BASE_EDITOR_CLASS_NAME, &class_name, -1);
821 
822         label = g_strdup_printf (_("Add %s"), class_name);
823 
824         item = gtk_menu_item_new_with_label (label);
825         gtk_widget_show (item);
826 
827         g_object_set_data (G_OBJECT (item), "object_type",
828                            GSIZE_TO_POINTER (iter_type));
829 
830         g_object_set_data (G_OBJECT (item), "object_add_mode",
831                            GINT_TO_POINTER (ADD_SIBLING));
832 
833         g_signal_connect (item, "activate",
834                           G_CALLBACK (glade_base_editor_add_item_activate),
835                           editor);
836         gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
837 
838         g_free (label);
839         g_free (class_name);
840 
841       }
842     while (gtk_tree_model_iter_next (model, &iter));
843 
844 
845   if ((model =
846        get_children_model_for_type (editor, G_OBJECT_TYPE (glade_widget_get_object (widget)))) &&
847       gtk_tree_model_get_iter_first (model, &iter))
848     do
849       {
850         gtk_tree_model_get (model, &iter,
851                             GLADE_BASE_EDITOR_GTYPE, &iter_type,
852                             GLADE_BASE_EDITOR_CLASS_NAME, &class_name, -1);
853 
854         label = g_strdup_printf (_("Add child %s"), class_name);
855 
856         item = gtk_menu_item_new_with_label (label);
857         gtk_widget_show (item);
858 
859         g_object_set_data (G_OBJECT (item), "object_type",
860                            GSIZE_TO_POINTER (iter_type));
861 
862         g_object_set_data (G_OBJECT (item), "object_add_mode",
863                            GINT_TO_POINTER (ADD_CHILD));
864 
865         g_signal_connect (item, "activate",
866                           G_CALLBACK (glade_base_editor_add_item_activate),
867                           editor);
868         gtk_menu_shell_append (GTK_MENU_SHELL (popup), item);
869 
870         g_free (label);
871         g_free (class_name);
872 
873       }
874     while (gtk_tree_model_iter_next (model, &iter));
875 
876   return popup;
877 }
878 
879 static gint
glade_base_editor_popup_handler(GtkWidget * treeview,GdkEventButton * event,GladeBaseEditor * e)880 glade_base_editor_popup_handler (GtkWidget       *treeview,
881                                  GdkEventButton  *event,
882                                  GladeBaseEditor *e)
883 {
884   GtkTreePath *path;
885   GtkWidget *popup;
886 
887   if (glade_popup_is_popup_event (event))
888     {
889       if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
890                                          (gint) event->x, (gint) event->y,
891                                          &path, NULL, NULL, NULL))
892         {
893           GtkTreeIter iter;
894           GladeWidget *gwidget;
895 
896           gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, NULL,
897                                     FALSE);
898 
899           gtk_tree_model_get_iter (e->priv->model, &iter, path);
900           gtk_tree_model_get (e->priv->model, &iter,
901                               GLADE_BASE_EDITOR_GWIDGET, &gwidget, -1);
902 
903 
904           popup = glade_base_editor_popup (e, gwidget);
905 
906           gtk_tree_path_free (path);
907 
908           gtk_menu_popup_at_pointer (GTK_MENU (popup), (GdkEvent*) event);
909         }
910       return TRUE;
911     }
912 
913   return FALSE;
914 }
915 
916 
917 static void
glade_base_editor_add_activate(GtkButton * button,GladeBaseEditor * e)918 glade_base_editor_add_activate (GtkButton *button, GladeBaseEditor *e)
919 {
920   if (e->priv->add_type)
921     glade_base_editor_add_child (e, e->priv->add_type, ADD_ROOT);
922 }
923 
924 static void
glade_base_editor_delete_child(GladeBaseEditor * e)925 glade_base_editor_delete_child (GladeBaseEditor *e)
926 {
927   GladeWidget *child, *gparent;
928   GtkTreeIter iter, parent;
929 
930   if (!glade_base_editor_get_child_selected (e, &iter))
931     return;
932 
933   gtk_tree_model_get (e->priv->model, &iter,
934                       GLADE_BASE_EDITOR_GWIDGET, &child, -1);
935 
936   if (gtk_tree_model_iter_parent (e->priv->model, &parent, &iter))
937     gtk_tree_model_get (e->priv->model, &parent,
938                         GLADE_BASE_EDITOR_GWIDGET, &gparent, -1);
939   else
940     gparent = e->priv->gcontainer;
941 
942   glade_command_push_group (_("Delete %s child from %s"),
943                             glade_widget_get_name (child),
944                             glade_widget_get_name (gparent));
945 
946   /* Emit delete-child signal */
947   glade_base_editor_delegate_delete_child (e, gparent, child);
948 
949   glade_command_pop_group ();
950 }
951 
952 
953 static gboolean
glade_base_editor_treeview_key_press_event(GtkWidget * widget,GdkEventKey * event,GladeBaseEditor * e)954 glade_base_editor_treeview_key_press_event (GtkWidget       *widget,
955                                             GdkEventKey     *event,
956                                             GladeBaseEditor *e)
957 {
958   if (event->keyval == GDK_KEY_Delete)
959     glade_base_editor_delete_child (e);
960 
961   return FALSE;
962 }
963 
964 static void
glade_base_editor_delete_activate(GtkButton * button,GladeBaseEditor * e)965 glade_base_editor_delete_activate (GtkButton *button, GladeBaseEditor *e)
966 {
967   glade_base_editor_delete_child (e);
968 }
969 
970 static gboolean
glade_base_editor_is_child(GladeBaseEditor * e,GladeWidget * gchild,gboolean valid_type)971 glade_base_editor_is_child (GladeBaseEditor *e,
972                             GladeWidget *gchild,
973                             gboolean valid_type)
974 {
975   GladeWidget *gcontainer = glade_widget_get_parent (gchild);
976 
977   if (!gcontainer)
978     return FALSE;
979 
980   if (valid_type)
981     {
982       GObject *child = glade_widget_get_object (gchild);
983 
984       if (glade_widget_get_internal (gchild) ||
985           glade_base_editor_get_type_info (e, NULL,
986                                            G_OBJECT_TYPE (child), -1) == FALSE)
987         return FALSE;
988 
989       gcontainer = e->priv->gcontainer;
990     }
991   else
992     {
993       GtkTreeIter iter;
994       if (glade_base_editor_get_child_selected (e, &iter))
995         gtk_tree_model_get (e->priv->model, &iter,
996                             GLADE_BASE_EDITOR_GWIDGET, &gcontainer, -1);
997       else
998         return FALSE;
999     }
1000 
1001   while ((gchild = glade_widget_get_parent (gchild)))
1002     if (gchild == gcontainer)
1003       return TRUE;
1004 
1005   return FALSE;
1006 }
1007 
1008 static gboolean
glade_base_editor_update_treeview_idle(gpointer data)1009 glade_base_editor_update_treeview_idle (gpointer data)
1010 {
1011   GladeBaseEditor *e = data;
1012   GList *selection = glade_project_selection_get (e->priv->project);
1013 
1014   glade_base_editor_block_callbacks (e, TRUE);
1015 
1016   glade_base_editor_fill_store (e);
1017   glade_base_editor_clear (e);
1018 
1019   gtk_tree_view_expand_all (GTK_TREE_VIEW (e->priv->treeview));
1020 
1021   if (selection)
1022     {
1023       GladeWidget *widget =
1024           glade_widget_get_from_gobject (G_OBJECT (selection->data));
1025       if (glade_base_editor_is_child (e, widget, TRUE))
1026         glade_base_editor_select_child (e, widget);
1027     }
1028 
1029   e->priv->updating_treeview = FALSE;
1030   glade_base_editor_block_callbacks (e, FALSE);
1031 
1032   return FALSE;
1033 }
1034 
1035 static void
glade_base_editor_project_widget_name_changed(GladeProject * project,GladeWidget * widget,GladeBaseEditor * editor)1036 glade_base_editor_project_widget_name_changed (GladeProject    *project,
1037                                                GladeWidget     *widget,
1038                                                GladeBaseEditor *editor)
1039 {
1040   GladeWidget *selected_child;
1041   GtkTreeIter iter;
1042 
1043   if (glade_base_editor_get_child_selected (editor, &iter))
1044     {
1045       gtk_tree_model_get (editor->priv->model, &iter,
1046                           GLADE_BASE_EDITOR_GWIDGET, &selected_child, -1);
1047       if (widget == selected_child)
1048         glade_base_editor_update_properties (editor);
1049 
1050       g_object_unref (G_OBJECT (selected_child));
1051     }
1052 }
1053 
1054 static void
glade_base_editor_project_closed(GladeProject * project,GladeBaseEditor * e)1055 glade_base_editor_project_closed (GladeProject *project, GladeBaseEditor *e)
1056 {
1057   glade_base_editor_set_container (e, NULL);
1058 }
1059 
1060 static void
glade_base_editor_reorder(GladeBaseEditor * editor,GtkTreeIter * iter)1061 glade_base_editor_reorder (GladeBaseEditor *editor, GtkTreeIter *iter)
1062 {
1063   GladeBaseEditorPrivate *e = editor->priv;
1064   GladeWidget *gchild, *gparent;
1065   GtkTreeIter parent_iter;
1066   gboolean retval;
1067 
1068   glade_command_push_group (_("Reorder %s's children"),
1069                             glade_widget_get_name (e->gcontainer));
1070 
1071   gtk_tree_model_get (e->model, iter, GLADE_BASE_EDITOR_GWIDGET, &gchild, -1);
1072   g_object_unref (G_OBJECT (gchild));
1073 
1074   if (gtk_tree_model_iter_parent (e->model, &parent_iter, iter))
1075     {
1076       gtk_tree_model_get (e->model, &parent_iter,
1077                           GLADE_BASE_EDITOR_GWIDGET, &gparent, -1);
1078       g_object_unref (G_OBJECT (gparent));
1079     }
1080   else
1081     gparent = e->gcontainer;
1082 
1083   g_signal_emit (editor, glade_base_editor_signals[SIGNAL_MOVE_CHILD],
1084                  0, gparent, gchild, &retval);
1085 
1086   if (retval)
1087     glade_base_editor_reorder_children (editor, iter);
1088   else
1089     {
1090       glade_base_editor_clear (editor);
1091       glade_base_editor_fill_store (editor);
1092       glade_base_editor_find_child (editor, gchild, &editor->priv->iter);
1093     }
1094 
1095   glade_command_pop_group ();
1096 }
1097 
1098 static gboolean
glade_base_editor_drag_and_drop_idle(gpointer data)1099 glade_base_editor_drag_and_drop_idle (gpointer data)
1100 {
1101   GladeBaseEditor *e = data;
1102 
1103   glade_base_editor_reorder (e, &e->priv->iter);
1104   gtk_tree_view_expand_all (GTK_TREE_VIEW (e->priv->treeview));
1105   glade_base_editor_set_cursor (e, &e->priv->iter);
1106   glade_base_editor_block_callbacks (e, FALSE);
1107 
1108   return FALSE;
1109 }
1110 
1111 static void
glade_base_editor_row_inserted(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,GladeBaseEditor * e)1112 glade_base_editor_row_inserted (GtkTreeModel    *model,
1113                                 GtkTreePath     *path,
1114                                 GtkTreeIter     *iter,
1115                                 GladeBaseEditor *e)
1116 {
1117   e->priv->iter = *iter;
1118   glade_base_editor_block_callbacks (e, TRUE);
1119   g_idle_add (glade_base_editor_drag_and_drop_idle, e);
1120 }
1121 
1122 static void
glade_base_editor_project_remove_widget(GladeProject * project,GladeWidget * widget,GladeBaseEditor * e)1123 glade_base_editor_project_remove_widget (GladeProject *project,
1124                                          GladeWidget *widget,
1125                                          GladeBaseEditor *e)
1126 {
1127   if (widget == e->priv->gcontainer)
1128     {
1129       glade_base_editor_set_container (e, NULL);
1130       return;
1131     }
1132 
1133   if (glade_base_editor_is_child (e, widget, TRUE))
1134     {
1135       GtkTreeIter iter;
1136       if (glade_base_editor_find_child (e, widget, &iter))
1137         {
1138           gtk_tree_store_remove (GTK_TREE_STORE (e->priv->model), &iter);
1139           glade_base_editor_clear (e);
1140         }
1141     }
1142 
1143   if (glade_widget_get_internal (widget) &&
1144       glade_base_editor_is_child (e, widget, FALSE))
1145     glade_base_editor_update_properties (e);
1146 }
1147 
1148 static void
glade_base_editor_project_add_widget(GladeProject * project,GladeWidget * widget,GladeBaseEditor * e)1149 glade_base_editor_project_add_widget (GladeProject    *project,
1150                                       GladeWidget     *widget,
1151                                       GladeBaseEditor *e)
1152 {
1153   if (e->priv->updating_treeview)
1154     return;
1155 
1156   if (glade_base_editor_is_child (e, widget, TRUE))
1157     {
1158       e->priv->updating_treeview = TRUE;
1159       g_idle_add (glade_base_editor_update_treeview_idle, e);
1160     }
1161 
1162   if (glade_widget_get_internal (widget) &&
1163       glade_base_editor_is_child (e, widget, FALSE))
1164     glade_base_editor_update_properties (e);
1165 }
1166 
1167 static gboolean
glade_base_editor_update_display_name(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1168 glade_base_editor_update_display_name (GtkTreeModel *model,
1169                                        GtkTreePath  *path,
1170                                        GtkTreeIter  *iter,
1171                                        gpointer      data)
1172 {
1173   GladeBaseEditor *editor = data;
1174   GladeWidget *gchild;
1175   gchar *name;
1176 
1177   gtk_tree_model_get (model, iter, GLADE_BASE_EDITOR_GWIDGET, &gchild, -1);
1178 
1179   name = glade_base_editor_get_display_name (editor, gchild);
1180 
1181   gtk_tree_store_set (GTK_TREE_STORE (editor->priv->model), iter,
1182                       GLADE_BASE_EDITOR_NAME, name, -1);
1183   g_free (name);
1184   g_object_unref (G_OBJECT (gchild));
1185 
1186   return FALSE;
1187 }
1188 
1189 static void
glade_base_editor_project_changed(GladeProject * project,GladeCommand * command,gboolean forward,GladeBaseEditor * editor)1190 glade_base_editor_project_changed (GladeProject    *project,
1191                                    GladeCommand    *command,
1192                                    gboolean         forward,
1193                                    GladeBaseEditor *editor)
1194 {
1195   gtk_tree_model_foreach (editor->priv->model,
1196                           glade_base_editor_update_display_name, editor);
1197 }
1198 
1199 
1200 
1201 static void
glade_base_editor_project_disconnect(GladeBaseEditor * editor)1202 glade_base_editor_project_disconnect (GladeBaseEditor *editor)
1203 {
1204   GladeBaseEditorPrivate *e = editor->priv;
1205 
1206   if (e->project == NULL)
1207     return;
1208 
1209   g_signal_handlers_disconnect_by_func (e->project,
1210                                         glade_base_editor_project_closed,
1211                                         editor);
1212 
1213   g_signal_handlers_disconnect_by_func (e->project,
1214                                         glade_base_editor_project_remove_widget,
1215                                         editor);
1216 
1217   g_signal_handlers_disconnect_by_func (e->project,
1218                                         glade_base_editor_project_add_widget,
1219                                         editor);
1220 
1221   g_signal_handlers_disconnect_by_func (e->project,
1222                                         glade_base_editor_project_widget_name_changed,
1223                                         editor);
1224 
1225   g_signal_handlers_disconnect_by_func (e->project,
1226                                         glade_base_editor_project_changed,
1227                                         editor);
1228 
1229 
1230   if (e->properties_idle)
1231     g_source_remove (e->properties_idle);
1232   e->properties_idle = 0;
1233 }
1234 
1235 static void
glade_base_editor_set_container(GladeBaseEditor * editor,GObject * container)1236 glade_base_editor_set_container (GladeBaseEditor *editor, GObject *container)
1237 {
1238   GladeBaseEditorPrivate *e = editor->priv;
1239 
1240   glade_base_editor_project_disconnect (editor);
1241 
1242   if (container == NULL)
1243     {
1244       reset_child_types (editor);
1245 
1246       e->gcontainer = NULL;
1247       e->project = NULL;
1248       glade_base_editor_block_callbacks (editor, TRUE);
1249       glade_base_editor_clear (editor);
1250 
1251       gtk_tree_view_set_model (GTK_TREE_VIEW (editor->priv->treeview), NULL);
1252       gtk_tree_store_clear (GTK_TREE_STORE (editor->priv->model));
1253       gtk_tree_view_set_model (GTK_TREE_VIEW (editor->priv->treeview),
1254                                editor->priv->model);
1255 
1256       gtk_widget_set_sensitive (e->paned, FALSE);
1257       glade_base_editor_block_callbacks (editor, FALSE);
1258 
1259       glade_signal_editor_load_widget (e->signal_editor, NULL);
1260 
1261       g_object_notify_by_pspec (G_OBJECT (editor), properties[PROP_CONTAINER]);
1262       return;
1263     }
1264 
1265   gtk_widget_set_sensitive (e->paned, TRUE);
1266 
1267   e->gcontainer = glade_widget_get_from_gobject (container);
1268 
1269   e->project = glade_widget_get_project (e->gcontainer);
1270 
1271   g_signal_connect (e->project, "close",
1272                     G_CALLBACK (glade_base_editor_project_closed), editor);
1273 
1274   g_signal_connect (e->project, "remove-widget",
1275                     G_CALLBACK (glade_base_editor_project_remove_widget),
1276                     editor);
1277 
1278   g_signal_connect (e->project, "add-widget",
1279                     G_CALLBACK (glade_base_editor_project_add_widget), editor);
1280 
1281   g_signal_connect (e->project, "widget-name-changed",
1282                     G_CALLBACK (glade_base_editor_project_widget_name_changed),
1283                     editor);
1284 
1285   g_signal_connect (e->project, "changed",
1286                     G_CALLBACK (glade_base_editor_project_changed), editor);
1287 
1288   g_object_notify_by_pspec (G_OBJECT (editor), properties[PROP_CONTAINER]);
1289 }
1290 
1291 /*************************** GladeBaseEditor Class ****************************/
1292 static void
glade_base_editor_dispose(GObject * object)1293 glade_base_editor_dispose (GObject *object)
1294 {
1295   GladeBaseEditor *cobj = GLADE_BASE_EDITOR (object);
1296 
1297   reset_child_types (cobj);
1298 
1299   glade_base_editor_project_disconnect (cobj);
1300   cobj->priv->project = NULL;
1301 
1302   G_OBJECT_CLASS (glade_base_editor_parent_class)->dispose (object);
1303 }
1304 
1305 static void
glade_base_editor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1306 glade_base_editor_set_property (GObject      *object,
1307                                 guint         prop_id,
1308                                 const GValue *value,
1309                                 GParamSpec   *pspec)
1310 {
1311   GladeBaseEditor *editor = GLADE_BASE_EDITOR (object);
1312 
1313   switch (prop_id)
1314     {
1315       case PROP_CONTAINER:
1316         glade_base_editor_set_container (editor, g_value_get_object (value));
1317         break;
1318       default:
1319         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1320         break;
1321     }
1322 }
1323 
1324 static void
glade_base_editor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1325 glade_base_editor_get_property (GObject    *object,
1326                                 guint       prop_id,
1327                                 GValue     *value,
1328                                 GParamSpec *pspec)
1329 {
1330   GladeBaseEditor *editor = GLADE_BASE_EDITOR (object);
1331 
1332   switch (prop_id)
1333     {
1334       case PROP_CONTAINER:
1335         g_value_set_object (value, glade_widget_get_object (editor->priv->gcontainer));
1336         break;
1337       default:
1338         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1339         break;
1340     }
1341 }
1342 
1343 
1344 /* Default handlers */
1345 static gboolean
glade_base_editor_change_type(GladeBaseEditor * editor,GladeWidget * gchild,GType type)1346 glade_base_editor_change_type (GladeBaseEditor *editor,
1347                                GladeWidget     *gchild,
1348                                GType            type)
1349 {
1350   GladeWidget *parent, *gchild_new;
1351   GList *children, *l;
1352   GObject *child_new;
1353   GtkTreeIter iter;
1354   gchar *name, *class_name;
1355 
1356   parent = glade_widget_get_parent (gchild);
1357 
1358   if (glade_base_editor_get_type_info (editor, NULL, type,
1359                                        GLADE_BASE_EDITOR_CLASS_NAME,
1360                                        &class_name, -1) == FALSE)
1361     return TRUE;
1362 
1363   name = g_strdup (glade_widget_get_name (gchild));
1364   glade_base_editor_find_child (editor, gchild, &iter);
1365 
1366   /* Delete old widget first, we cant assume the old and new widget can live in
1367    * the same parent simultaniously */
1368   glade_base_editor_delegate_delete_child (editor, parent, gchild);
1369 
1370   /* Create new widget */
1371   gchild_new = glade_base_editor_delegate_build_child (editor, parent, type);
1372 
1373   child_new = glade_widget_get_object (gchild_new);
1374 
1375   /* Cut and Paste childrens */
1376   if ((children = glade_widget_get_children (gchild)) != NULL)
1377     {
1378       GList *gchildren = NULL;
1379 
1380       l = children;
1381       while (l)
1382         {
1383           GladeWidget *w = glade_widget_get_from_gobject (l->data);
1384 
1385           if (w && !glade_widget_get_internal (w))
1386             gchildren = g_list_prepend (gchildren, w);
1387 
1388           l = g_list_next (l);
1389         }
1390 
1391       if (gchildren)
1392         {
1393           glade_command_dnd (gchildren, gchild_new, NULL);
1394 
1395           g_list_free (children);
1396           g_list_free (gchildren);
1397         }
1398     }
1399 
1400   /* Copy properties */
1401   glade_widget_copy_properties (gchild_new, gchild, TRUE, TRUE);
1402 
1403   /* Apply packing properties to the new object
1404    *
1405    * No need to use GladeCommand here on the newly created widget,
1406    * they just become the initial state for this object.
1407    */
1408   l = glade_widget_get_packing_properties (gchild);
1409   while (l)
1410     {
1411       GladeProperty      *orig_prop = (GladeProperty *) l->data;
1412       GladePropertyClass *pclass = glade_property_get_class (orig_prop);
1413       GladeProperty      *dup_prop = glade_widget_get_property (gchild_new, glade_property_class_id (pclass));
1414       glade_property_set_value (dup_prop, glade_property_inline_value (orig_prop));
1415       l = g_list_next (l);
1416     }
1417 
1418   /* Set the name */
1419   glade_command_set_name (gchild_new, name);
1420 
1421   if (GTK_IS_WIDGET (child_new))
1422     gtk_widget_show_all (GTK_WIDGET (child_new));
1423 
1424   /* XXX We should update the widget name in the visible tree here too */
1425   gtk_tree_store_set (GTK_TREE_STORE (editor->priv->model), &iter,
1426                       GLADE_BASE_EDITOR_GWIDGET, gchild_new,
1427                       GLADE_BASE_EDITOR_OBJECT, child_new,
1428                       GLADE_BASE_EDITOR_TYPE_NAME, class_name, -1);
1429   g_free (class_name);
1430   g_free (name);
1431 
1432   return TRUE;
1433 }
1434 
1435 static gchar *
glade_base_editor_get_display_name_impl(GladeBaseEditor * editor,GladeWidget * gchild)1436 glade_base_editor_get_display_name_impl (GladeBaseEditor *editor,
1437                                          GladeWidget     *gchild)
1438 {
1439   return g_strdup (glade_widget_get_display_name (gchild));
1440 }
1441 
1442 static GladeWidget *
glade_base_editor_build_child(GladeBaseEditor * editor,GladeWidget * gparent,GType type)1443 glade_base_editor_build_child (GladeBaseEditor *editor,
1444                                GladeWidget *gparent,
1445                                GType type)
1446 {
1447   return glade_command_create (glade_widget_adaptor_get_by_type (type),
1448                                gparent, NULL,
1449                                glade_widget_get_project (gparent));
1450 }
1451 
1452 static gboolean
glade_base_editor_move_child(GladeBaseEditor * editor,GladeWidget * gparent,GladeWidget * gchild)1453 glade_base_editor_move_child (GladeBaseEditor *editor,
1454                               GladeWidget     *gparent,
1455                               GladeWidget     *gchild)
1456 {
1457   GList list = { 0, };
1458 
1459   if (gparent != glade_widget_get_parent (gchild))
1460     {
1461       list.data = gchild;
1462       glade_command_dnd (&list, gparent, NULL);
1463     }
1464 
1465   return TRUE;
1466 }
1467 
1468 static gboolean
glade_base_editor_delete_child_impl(GladeBaseEditor * editor,GladeWidget * gparent,GladeWidget * gchild)1469 glade_base_editor_delete_child_impl (GladeBaseEditor *editor,
1470                                      GladeWidget     *gparent,
1471                                      GladeWidget     *gchild)
1472 {
1473   GList list = { 0, };
1474 
1475   list.data = gchild;
1476   glade_command_delete (&list);
1477 
1478   return TRUE;
1479 }
1480 
1481 static void
glade_base_editor_block_callbacks(GladeBaseEditor * editor,gboolean block)1482 glade_base_editor_block_callbacks (GladeBaseEditor *editor, gboolean block)
1483 {
1484   GladeBaseEditorPrivate *e = editor->priv;
1485   if (block)
1486     {
1487       g_signal_handlers_block_by_func (e->model, glade_base_editor_row_inserted,
1488                                        editor);
1489       if (e->project)
1490         {
1491           g_signal_handlers_block_by_func (e->project,
1492                                            glade_base_editor_project_remove_widget,
1493                                            editor);
1494           g_signal_handlers_block_by_func (e->project,
1495                                            glade_base_editor_project_add_widget,
1496                                            editor);
1497           g_signal_handlers_block_by_func (e->project,
1498                                            glade_base_editor_project_changed,
1499                                            editor);
1500         }
1501     }
1502   else
1503     {
1504       g_signal_handlers_unblock_by_func (e->model,
1505                                          glade_base_editor_row_inserted,
1506                                          editor);
1507       if (e->project)
1508         {
1509           g_signal_handlers_unblock_by_func (e->project,
1510                                              glade_base_editor_project_remove_widget,
1511                                              editor);
1512           g_signal_handlers_unblock_by_func (e->project,
1513                                              glade_base_editor_project_add_widget,
1514                                              editor);
1515           g_signal_handlers_unblock_by_func (e->project,
1516                                              glade_base_editor_project_changed,
1517                                              editor);
1518         }
1519     }
1520 }
1521 
1522 static void
glade_base_editor_realize_callback(GtkWidget * widget,gpointer user_data)1523 glade_base_editor_realize_callback (GtkWidget *widget, gpointer user_data)
1524 {
1525   GladeBaseEditor *editor = GLADE_BASE_EDITOR (widget);
1526 
1527   glade_base_editor_block_callbacks (editor, TRUE);
1528 
1529   glade_base_editor_fill_store (editor);
1530   gtk_tree_view_expand_all (GTK_TREE_VIEW (editor->priv->treeview));
1531 
1532   glade_base_editor_block_callbacks (editor, FALSE);
1533 }
1534 
1535 static void
glade_base_editor_init(GladeBaseEditor * editor)1536 glade_base_editor_init (GladeBaseEditor *editor)
1537 {
1538   GladeBaseEditorPrivate *e;
1539   GtkCellRenderer *renderer;
1540   GtkTreeViewColumn *column;
1541 
1542   gtk_widget_init_template (GTK_WIDGET (editor));
1543 
1544   e = editor->priv = glade_base_editor_get_instance_private (editor);
1545 
1546   renderer = gtk_cell_renderer_text_new ();
1547   column = gtk_tree_view_column_new_with_attributes (_("Label"), renderer,
1548                                                      "text",
1549                                                      GLADE_BASE_EDITOR_NAME,
1550                                                      NULL);
1551   gtk_tree_view_append_column (GTK_TREE_VIEW (e->treeview), column);
1552 
1553   renderer = gtk_cell_renderer_combo_new ();
1554   g_object_set (renderer,
1555                 "has-entry", FALSE,
1556                 "text-column", GLADE_BASE_EDITOR_CLASS_NAME,
1557                 "editable", TRUE, NULL);
1558 
1559   g_signal_connect (G_OBJECT (renderer), "edited",
1560                     G_CALLBACK (glade_base_editor_child_type_edited), editor);
1561 
1562   column = gtk_tree_view_column_new_with_attributes (_("Type"), renderer,
1563                                                      "text",
1564                                                      GLADE_BASE_EDITOR_TYPE_NAME,
1565                                                      "model",
1566                                                      GLADE_BASE_EDITOR_CHILD_TYPES,
1567                                                      NULL);
1568 
1569   gtk_tree_view_append_column (GTK_TREE_VIEW (e->treeview), column);
1570 }
1571 
1572 static void
glade_base_editor_class_init(GladeBaseEditorClass * klass)1573 glade_base_editor_class_init (GladeBaseEditorClass *klass)
1574 {
1575   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1576   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1577 
1578   glade_base_editor_parent_class = g_type_class_peek_parent (klass);
1579 
1580   object_class->dispose = glade_base_editor_dispose;
1581   object_class->set_property = glade_base_editor_set_property;
1582   object_class->get_property = glade_base_editor_get_property;
1583 
1584   klass->change_type = glade_base_editor_change_type;
1585   klass->get_display_name = glade_base_editor_get_display_name_impl;
1586   klass->build_child = glade_base_editor_build_child;
1587   klass->delete_child = glade_base_editor_delete_child_impl;
1588   klass->move_child = glade_base_editor_move_child;
1589 
1590   properties[PROP_CONTAINER] =
1591     g_param_spec_object ("container",
1592                          _("Container"),
1593                          _("The container object this editor is currently editing"),
1594                          G_TYPE_OBJECT,
1595                          G_PARAM_READWRITE);
1596 
1597   /* Install all properties */
1598   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
1599 
1600   /**
1601    * GladeBaseEditor::child-selected:
1602    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1603    * @gchild: the selected #GladeWidget.
1604    *
1605    * Emited when the user selects a child in the editor's treeview.
1606    * You can add the relevant child properties here using
1607    * glade_base_editor_add_default_properties() and glade_base_editor_add_properties()
1608    * You can also add labels with glade_base_editor_add_label to make the
1609    * editor look pretty.
1610    */
1611   glade_base_editor_signals[SIGNAL_CHILD_SELECTED] =
1612       g_signal_new ("child-selected",
1613                     G_TYPE_FROM_CLASS (object_class),
1614                     G_SIGNAL_RUN_LAST,
1615                     G_STRUCT_OFFSET (GladeBaseEditorClass, child_selected),
1616                     NULL, NULL,
1617                     _glade_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT);
1618 
1619   /**
1620    * GladeBaseEditor::child-change-type:
1621    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1622    * @child: the #GObject being changed.
1623    * @type: the new type for @child.
1624    *
1625    * Returns: TRUE to stop signal emision.
1626    */
1627   glade_base_editor_signals[SIGNAL_CHANGE_TYPE] =
1628       g_signal_new ("change-type",
1629                     G_TYPE_FROM_CLASS (object_class),
1630                     G_SIGNAL_RUN_LAST,
1631                     G_STRUCT_OFFSET (GladeBaseEditorClass, change_type),
1632                     _glade_boolean_handled_accumulator, NULL, NULL,
1633                     G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_GTYPE);
1634 
1635   /**
1636    * GladeBaseEditor::get-display-name:
1637    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1638    * @gchild: the child to get display name string to show in @gladebaseeditor
1639    * treeview.
1640    *
1641    * Returns: a newly allocated string.
1642    */
1643   glade_base_editor_signals[SIGNAL_GET_DISPLAY_NAME] =
1644       g_signal_new ("get-display-name",
1645                     G_TYPE_FROM_CLASS (object_class),
1646                     G_SIGNAL_RUN_LAST,
1647                     G_STRUCT_OFFSET (GladeBaseEditorClass, get_display_name),
1648                     _glade_string_accumulator, NULL,
1649                     _glade_marshal_STRING__OBJECT,
1650                     G_TYPE_STRING, 1, G_TYPE_OBJECT);
1651 
1652   /**
1653    * GladeBaseEditor::build-child:
1654    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1655    * @gparent: the parent of the new child
1656    * @type: the #GType of the child
1657    *
1658    * Create a child widget here if something else must be done other than
1659    * calling glade_command_create() such as creating an intermediate parent.
1660    *
1661    * Returns: the newly created #GladeWidget or NULL if child cant be created
1662    */
1663   glade_base_editor_signals[SIGNAL_BUILD_CHILD] =
1664       g_signal_new ("build-child",
1665                     G_TYPE_FROM_CLASS (object_class),
1666                     G_SIGNAL_RUN_LAST,
1667                     G_STRUCT_OFFSET (GladeBaseEditorClass, build_child),
1668                     _glade_stop_emission_accumulator, NULL, NULL,
1669                     G_TYPE_OBJECT, 2, G_TYPE_OBJECT, G_TYPE_GTYPE);
1670 
1671   /**
1672    * GladeBaseEditor::delete-child:
1673    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1674    * @gparent: the parent
1675    * @gchild: the child to delete
1676    */
1677   glade_base_editor_signals[SIGNAL_DELETE_CHILD] =
1678       g_signal_new ("delete-child",
1679                     G_TYPE_FROM_CLASS (object_class),
1680                     G_SIGNAL_RUN_LAST,
1681                     G_STRUCT_OFFSET (GladeBaseEditorClass, delete_child),
1682                     _glade_boolean_handled_accumulator, NULL,
1683                     _glade_marshal_BOOLEAN__OBJECT_OBJECT,
1684                     G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
1685 
1686   /**
1687    * GladeBaseEditor::move-child:
1688    * @gladebaseeditor: the #GladeBaseEditor which received the signal.
1689    * @gparent: the new parent of @gchild
1690    * @gchild: the #GladeWidget to move
1691    *
1692    * Move child here if something else must be done other than cut & paste.
1693    *
1694    * Returns: wheater child has been sucessfully moved or not.
1695    */
1696   glade_base_editor_signals[SIGNAL_MOVE_CHILD] =
1697       g_signal_new ("move-child",
1698                     G_TYPE_FROM_CLASS (object_class),
1699                     G_SIGNAL_RUN_LAST,
1700                     G_STRUCT_OFFSET (GladeBaseEditorClass, move_child),
1701                     _glade_stop_emission_accumulator, NULL,
1702                     _glade_marshal_BOOLEAN__OBJECT_OBJECT,
1703                     G_TYPE_BOOLEAN, 2, G_TYPE_OBJECT, G_TYPE_OBJECT);
1704 
1705   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/gladeui/glade-base-editor.ui");
1706   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, paned);
1707   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, treeview);
1708   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, add_button);
1709   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, delete_button);
1710   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, help_button);
1711   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, table);
1712   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, signal_editor);
1713   gtk_widget_class_bind_template_child_private (widget_class, GladeBaseEditor, tip_label);
1714   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_realize_callback);
1715   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_treeview_cursor_changed);
1716   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_popup_handler);
1717   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_treeview_key_press_event);
1718   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_add_activate);
1719   gtk_widget_class_bind_template_callback (widget_class, glade_base_editor_delete_activate);
1720 }
1721 
1722 /********************************* Public API *********************************/
1723 /**
1724  * glade_base_editor_new:
1725  * @container: a container this new editor will edit.
1726  * @main_editable: the custom #GladeEditable for @container, or %NULL
1727  * @... A NULL terminated list of gchar *, GType
1728  *
1729  * Creates a new GladeBaseEditor with @container toplevel
1730  * support for all the object types indicated in the variable argument list.
1731  * Argument List:
1732  *   o The type name
1733  *   o The GType the editor will support
1734  *
1735  * Returns: a new GladeBaseEditor.
1736  */
1737 GladeBaseEditor *
glade_base_editor_new(GObject * container,GladeEditable * main_editable,...)1738 glade_base_editor_new (GObject *container, GladeEditable *main_editable, ...)
1739 {
1740   ChildTypeTab *child_type;
1741   GladeWidget *gcontainer;
1742   GladeBaseEditor *editor;
1743   GladeBaseEditorPrivate *e;
1744   GtkTreeIter iter;
1745   GType iter_type;
1746   gchar *name;
1747   va_list args;
1748 
1749   gcontainer = glade_widget_get_from_gobject (container);
1750   g_return_val_if_fail (GLADE_IS_WIDGET (gcontainer), NULL);
1751 
1752   editor = GLADE_BASE_EDITOR (g_object_new (GLADE_TYPE_BASE_EDITOR, NULL));
1753   e = editor->priv;
1754 
1755   /* Store */
1756   e->model = (GtkTreeModel *) gtk_tree_store_new (GLADE_BASE_EDITOR_N_COLUMNS,
1757                                                   G_TYPE_OBJECT,
1758                                                   G_TYPE_OBJECT,
1759                                                   G_TYPE_STRING,
1760                                                   G_TYPE_STRING,
1761                                                   GTK_TYPE_TREE_MODEL);
1762 
1763   gtk_tree_view_set_model (GTK_TREE_VIEW (e->treeview), e->model);
1764   gtk_tree_view_expand_all (GTK_TREE_VIEW (e->treeview));
1765 
1766   g_signal_connect (e->model, "row-inserted",
1767                     G_CALLBACK (glade_base_editor_row_inserted), editor);
1768 
1769   if (main_editable)
1770     g_warning ("%s main_editable is deprecated, the editor will only show the hierarchy editor", __func__);
1771 
1772   child_type = g_new0 (ChildTypeTab, 1);
1773   child_type->parent_type = G_OBJECT_TYPE (container);
1774   child_type->children =
1775       (GtkTreeModel *) gtk_list_store_new (GLADE_BASE_EDITOR_TYPES_N_COLUMNS,
1776                                            G_TYPE_GTYPE, G_TYPE_STRING);
1777 
1778   va_start (args, main_editable);
1779   while ((name = va_arg (args, gchar *)))
1780     {
1781       iter_type = va_arg (args, GType);
1782 
1783       gtk_list_store_append (GTK_LIST_STORE (child_type->children), &iter);
1784       gtk_list_store_set (GTK_LIST_STORE (child_type->children), &iter,
1785                           GLADE_BASE_EDITOR_GTYPE, iter_type,
1786                           GLADE_BASE_EDITOR_CLASS_NAME, name, -1);
1787 
1788       if (editor->priv->add_type == 0)
1789         editor->priv->add_type = iter_type;
1790     }
1791   va_end (args);
1792 
1793   e->child_types = g_list_prepend (e->child_types, child_type);
1794 
1795   glade_base_editor_set_container (editor, container);
1796 
1797   glade_signal_editor_load_widget (e->signal_editor, e->gcontainer);
1798 
1799   return editor;
1800 }
1801 
1802 
1803 
1804 /**
1805  * glade_base_editor_append_types:
1806  * @editor: A #GladeBaseEditor
1807  * @parent_type: the parent type these child types will apply to
1808  * @... A NULL terminated list of gchar *, GType
1809  *
1810  * Appends support for all the object types indicated in the variable argument list.
1811  * Argument List:
1812  *   o The type name
1813  *   o The GType the editor will support for parents of type @type
1814  *
1815  */
1816 void
glade_base_editor_append_types(GladeBaseEditor * editor,GType parent_type,...)1817 glade_base_editor_append_types (GladeBaseEditor *editor, GType parent_type, ...)
1818 {
1819   ChildTypeTab *child_type;
1820   GtkTreeIter iter;
1821   gchar *name;
1822   va_list args;
1823 
1824   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
1825   g_return_if_fail (get_children_model_for_type (editor, parent_type) == NULL);
1826 
1827   child_type = g_new0 (ChildTypeTab, 1);
1828   child_type->parent_type = parent_type;
1829   child_type->children =
1830       (GtkTreeModel *) gtk_list_store_new (GLADE_BASE_EDITOR_TYPES_N_COLUMNS,
1831                                            G_TYPE_GTYPE, G_TYPE_STRING);
1832 
1833   va_start (args, parent_type);
1834   while ((name = va_arg (args, gchar *)))
1835     {
1836       gtk_list_store_append (GTK_LIST_STORE (child_type->children), &iter);
1837       gtk_list_store_set (GTK_LIST_STORE (child_type->children), &iter,
1838                           GLADE_BASE_EDITOR_GTYPE, va_arg (args, GType),
1839                           GLADE_BASE_EDITOR_CLASS_NAME, name,
1840                           -1);
1841     }
1842   va_end (args);
1843 
1844   editor->priv->child_types =
1845       g_list_insert_sorted (editor->priv->child_types, child_type,
1846                             (GCompareFunc) sort_type_by_hierarchy);
1847 }
1848 
1849 /**
1850  * glade_base_editor_add_default_properties:
1851  * @editor: a #GladeBaseEditor
1852  * @gchild: a #GladeWidget
1853  *
1854  * Add @gchild name and type property to @editor
1855  *
1856  * NOTE: This function is intended to be used in "child-selected" callbacks
1857  */
1858 void
glade_base_editor_add_default_properties(GladeBaseEditor * editor,GladeWidget * gchild)1859 glade_base_editor_add_default_properties (GladeBaseEditor *editor,
1860                                           GladeWidget     *gchild)
1861 {
1862   GtkTreeIter combo_iter;
1863   GtkWidget *label, *entry;
1864   GtkTreeModel *child_class;
1865   GtkCellRenderer *renderer;
1866   GObject *child;
1867 
1868   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
1869   g_return_if_fail (GLADE_IS_WIDGET (gchild));
1870   g_return_if_fail (GLADE_IS_WIDGET (glade_widget_get_parent (gchild)));
1871 
1872   child = glade_widget_get_object (gchild);
1873 
1874   child_class =
1875       get_children_model_for_child_type (editor, G_OBJECT_TYPE (child));
1876 
1877   /* Name */
1878   label = gtk_label_new (_("ID:"));
1879   gtk_widget_set_halign (label, GTK_ALIGN_END);
1880   gtk_widget_set_valign (label, GTK_ALIGN_START);
1881 
1882   entry = gtk_entry_new ();
1883   if (glade_widget_has_name (gchild))
1884     gtk_entry_set_text (GTK_ENTRY (entry), glade_widget_get_name (gchild));
1885   else
1886     gtk_entry_set_text (GTK_ENTRY (entry), "");
1887 
1888   g_object_set_data (G_OBJECT (entry), "editor", editor);
1889   g_signal_connect (entry, "activate",
1890                     G_CALLBACK (glade_base_editor_name_activate), gchild);
1891   g_signal_connect (entry, "changed",
1892                     G_CALLBACK (glade_base_editor_name_activate), gchild);
1893   glade_base_editor_table_attach (editor, label, entry);
1894 
1895   if (child_class && gtk_tree_model_iter_n_children (child_class, NULL) > 1)
1896     {
1897       /* Type */
1898       label = gtk_label_new (_("Type:"));
1899       gtk_widget_set_halign (label, GTK_ALIGN_END);
1900       gtk_widget_set_valign (label, GTK_ALIGN_START);
1901 
1902       entry = gtk_combo_box_new ();
1903       gtk_combo_box_set_model (GTK_COMBO_BOX (entry), child_class);
1904 
1905       renderer = gtk_cell_renderer_text_new ();
1906       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (entry), renderer, FALSE);
1907       gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (entry), renderer, "text",
1908                                       GLADE_BASE_EDITOR_CLASS_NAME, NULL);
1909 
1910       if (glade_base_editor_get_type_info (editor, &combo_iter,
1911                                            G_OBJECT_TYPE (child), -1))
1912         gtk_combo_box_set_active_iter (GTK_COMBO_BOX (entry), &combo_iter);
1913 
1914       g_signal_connect (entry, "changed",
1915                         G_CALLBACK (glade_base_editor_type_changed), editor);
1916       glade_base_editor_table_attach (editor, label, entry);
1917     }
1918 }
1919 
1920 /**
1921  * glade_base_editor_add_properties:
1922  * @editor: a #GladeBaseEditor
1923  * @gchild: a #GladeWidget
1924  * @packing: whether we are adding packing properties or not
1925  * @...: A NULL terminated list of properties names.
1926  *
1927  * Add @gchild properties to @editor
1928  *
1929  * NOTE: This function is intended to be used in "child-selected" callbacks
1930  */
1931 void
glade_base_editor_add_properties(GladeBaseEditor * editor,GladeWidget * gchild,gboolean packing,...)1932 glade_base_editor_add_properties (GladeBaseEditor *editor,
1933                                   GladeWidget     *gchild,
1934                                   gboolean         packing,
1935                                   ...)
1936 {
1937   GladeEditorProperty *eprop;
1938   va_list args;
1939   gchar *property;
1940 
1941   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
1942   g_return_if_fail (GLADE_IS_WIDGET (gchild));
1943 
1944   va_start (args, packing);
1945   property = va_arg (args, gchar *);
1946 
1947   while (property)
1948     {
1949       eprop =
1950           glade_widget_create_editor_property (gchild, property, packing, TRUE);
1951       if (eprop)
1952         glade_base_editor_table_attach (editor,
1953                                         glade_editor_property_get_item_label (eprop),
1954                                         GTK_WIDGET (eprop));
1955       property = va_arg (args, gchar *);
1956     }
1957   va_end (args);
1958 }
1959 
1960 
1961 /**
1962  * glade_base_editor_add_editable:
1963  * @editor: a #GladeBaseEditor
1964  * @gchild: the #GladeWidget
1965  * @page: the #GladeEditorPageType of the desired page for @gchild
1966  *
1967  * Add @gchild editor of type @page to the base editor
1968  *
1969  * NOTE: This function is intended to be used in "child-selected" callbacks
1970  */
1971 void
glade_base_editor_add_editable(GladeBaseEditor * editor,GladeWidget * gchild,GladeEditorPageType page)1972 glade_base_editor_add_editable (GladeBaseEditor    *editor,
1973                                 GladeWidget        *gchild,
1974                                 GladeEditorPageType page)
1975 {
1976   GladeEditable *editable;
1977   gint row;
1978 
1979   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
1980   g_return_if_fail (GLADE_IS_WIDGET (gchild));
1981 
1982   editable = glade_widget_adaptor_create_editable (glade_widget_get_adaptor (gchild), page);
1983   glade_editable_set_show_name (editable, FALSE);
1984   glade_editable_load (editable, gchild);
1985   gtk_widget_show (GTK_WIDGET (editable));
1986 
1987   row = editor->priv->row;
1988 
1989   gtk_grid_attach (GTK_GRID (editor->priv->table), GTK_WIDGET (editable), 0,
1990                    row, 2, 1);
1991   gtk_widget_set_hexpand (GTK_WIDGET (editable), TRUE);
1992 
1993   editor->priv->row++;
1994 
1995   gtk_widget_hide (editor->priv->tip_label);
1996 }
1997 
1998 
1999 
2000 /**
2001  * glade_base_editor_add_label:
2002  * @editor: a #GladeBaseEditor
2003  * @str: the label string
2004  *
2005  * Adds a new label to @editor
2006  *
2007  * NOTE: This function is intended to be used in "child-selected" callbacks
2008  */
2009 void
glade_base_editor_add_label(GladeBaseEditor * editor,gchar * str)2010 glade_base_editor_add_label (GladeBaseEditor *editor, gchar *str)
2011 {
2012   GtkWidget *label;
2013   gchar *markup;
2014   gint row;
2015 
2016   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
2017   g_return_if_fail (str != NULL);
2018 
2019   label = gtk_label_new (NULL);
2020   markup = g_strdup_printf ("<span rise=\"-20000\"><b>%s</b></span>", str);
2021   row = editor->priv->row;
2022 
2023   gtk_label_set_markup (GTK_LABEL (label), markup);
2024   gtk_widget_set_halign (label, GTK_ALIGN_START);
2025   gtk_widget_set_valign (label, GTK_ALIGN_START);
2026   gtk_widget_set_margin_top (label, 6);
2027   gtk_widget_set_margin_bottom (label, 6);
2028 
2029   gtk_grid_attach (GTK_GRID (editor->priv->table), label, 0, row, 2, 1);
2030   gtk_widget_show (label);
2031   editor->priv->row++;
2032 
2033   gtk_widget_hide (editor->priv->tip_label);
2034   g_free (markup);
2035 }
2036 
2037 /**
2038  * glade_base_editor_set_show_signal_editor:
2039  * @editor: a #GladeBaseEditor
2040  * @val:
2041  *
2042  * Shows/hide @editor 's signal editor
2043  */
2044 void
glade_base_editor_set_show_signal_editor(GladeBaseEditor * editor,gboolean val)2045 glade_base_editor_set_show_signal_editor (GladeBaseEditor *editor, gboolean val)
2046 {
2047   g_return_if_fail (GLADE_IS_BASE_EDITOR (editor));
2048 
2049   if (val)
2050     gtk_widget_show (GTK_WIDGET (editor->priv->signal_editor));
2051   else
2052     gtk_widget_hide (GTK_WIDGET (editor->priv->signal_editor));
2053 }
2054 
2055 /* Convenience functions */
2056 
2057 static void
glade_base_editor_help(GtkButton * button,gchar * markup)2058 glade_base_editor_help (GtkButton *button, gchar *markup)
2059 {
2060   GtkWidget *dialog;
2061 
2062   dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
2063                                    GTK_DIALOG_DESTROY_WITH_PARENT,
2064                                    GTK_MESSAGE_INFO, GTK_BUTTONS_OK, " ");
2065 
2066   gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), markup);
2067 
2068   gtk_dialog_run (GTK_DIALOG (dialog));
2069   gtk_widget_destroy (dialog);
2070 }
2071 
2072 /**
2073  * glade_base_editor_pack_new_window:
2074  * @editor: a #GladeBaseEditor
2075  * @title: the window title
2076  * @help_markup: the help text
2077  *
2078  * This convenience function create a new dialog window and packs @editor in it.
2079  *
2080  * Returns: the newly created window
2081  */
2082 GtkWidget *
glade_base_editor_pack_new_window(GladeBaseEditor * editor,gchar * title,gchar * help_markup)2083 glade_base_editor_pack_new_window (GladeBaseEditor *editor,
2084                                    gchar           *title,
2085                                    gchar           *help_markup)
2086 {
2087   GtkWidget *window, *headerbar;
2088   gchar *msg;
2089 
2090   g_return_val_if_fail (GLADE_IS_BASE_EDITOR (editor), NULL);
2091 
2092   /* Window */
2093   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2094   headerbar = gtk_header_bar_new ();
2095   gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
2096   gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
2097   gtk_widget_show (headerbar);
2098 
2099   if (title)
2100     {
2101       const gchar *name = glade_widget_get_display_name (editor->priv->gcontainer);
2102 
2103       gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), title);
2104       gtk_header_bar_set_subtitle (GTK_HEADER_BAR (headerbar), name);
2105     }
2106 
2107   g_signal_connect_swapped (G_OBJECT (editor), "notify::container",
2108                             G_CALLBACK (gtk_widget_destroy), window);
2109 
2110   msg = help_markup ? help_markup :
2111         _("<big><b>Tips:</b></big>\n"
2112           "  * Right-click over the treeview to add items.\n"
2113           "  * Press Delete to remove the selected item.\n"
2114           "  * Drag &amp; Drop to reorder.\n"
2115           "  * Type column is editable.");
2116 
2117   gtk_label_set_markup (GTK_LABEL (editor->priv->tip_label), msg);
2118   g_signal_connect (editor->priv->help_button, "clicked",
2119                     G_CALLBACK (glade_base_editor_help),
2120                     msg);
2121 
2122   gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (editor));
2123   gtk_widget_show_all (GTK_WIDGET (editor));
2124 
2125   gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
2126 
2127   return window;
2128 }
2129