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 & 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