glade-widget.c 116 KB
Newer Older
Jose Maria Celorio's avatar
Jose Maria Celorio committed
1
2
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
3
 * Copyright (C) 2008 Tristan Van Berkom
4
5
 * Copyright (C) 2004 Joaquin Cuenca Abela
 * Copyright (C) 2001, 2002, 2003 Ximian, Inc.
Jose Maria Celorio's avatar
Jose Maria Celorio committed
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Jose Maria Celorio's avatar
Jose Maria Celorio committed
20
21
 *
 * Authors:
22
 *   Joaquin Cuenca Abela <e98cuenc@yahoo.com>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
23
 *   Chema Celorio <chema@celorio.com>
24
 *   Tristan Van Berkom <tvb@gnome.org>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
25
26
 */

27
28
29
30
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

31
32
33
34
35
36
37
38
39
/**
 * SECTION:glade-widget
 * @Short_Description: An object wrapper for the Glade runtime environment.
 *
 * #GladeWidget is the proxy between the instantiated runtime object and
 * the Glade core metadata. This api will be mostly usefull for its
 * convenience api for getting and setting properties (mostly from the plugin).
 */

Jose Maria Celorio's avatar
Jose Maria Celorio committed
40
#include <string.h>
41
#include <glib-object.h>
42
43
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n-lib.h>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
44
#include "glade.h"
45
#include "glade-accumulators.h"
Jose Maria Celorio's avatar
Jose Maria Celorio committed
46
#include "glade-project.h"
47
#include "glade-widget-adaptor.h"
48
49
#include "glade-widget.h"
#include "glade-marshallers.h"
Jose Maria Celorio's avatar
Jose Maria Celorio committed
50
51
#include "glade-property.h"
#include "glade-property-class.h"
52
#include "glade-placeholder.h"
53
#include "glade-signal.h"
54
#include "glade-popup.h"
55
#include "glade-editor.h"
56
#include "glade-app.h"
57
#include "glade-design-view.h"
58
#include "glade-widget-action.h"
59

Jose Maria Celorio's avatar
Jose Maria Celorio committed
60

61

62
63
static void         glade_widget_set_adaptor           (GladeWidget           *widget,
							GladeWidgetAdaptor    *adaptor);
64
65
static void         glade_widget_set_properties        (GladeWidget           *widget,
							GList                 *properties);
66
67
68
static void         glade_widget_set_object            (GladeWidget           *gwidget, 
							GObject               *new_object, 
							gboolean               destroy);
69

70
enum
Jose Maria Celorio's avatar
Jose Maria Celorio committed
71
{
72
73
74
	ADD_SIGNAL_HANDLER,
	REMOVE_SIGNAL_HANDLER,
	CHANGE_SIGNAL_HANDLER,
75
76
77
	BUTTON_PRESS_EVENT,
	BUTTON_RELEASE_EVENT,
	MOTION_NOTIFY_EVENT,
78
	SUPPORT_CHANGED,
79
80
	LAST_SIGNAL
};
Jose Maria Celorio's avatar
Jose Maria Celorio committed
81

82
enum
83
{
84
85
86
	PROP_0,
	PROP_NAME,
	PROP_INTERNAL,
87
	PROP_ANARCHIST,
88
	PROP_OBJECT,
89
	PROP_ADAPTOR,
90
91
	PROP_PROJECT,
	PROP_PROPERTIES,
92
93
94
	PROP_PARENT,
	PROP_INTERNAL_NAME,
	PROP_TEMPLATE,
95
	PROP_TEMPLATE_EXACT,
96
97
	PROP_REASON,
	PROP_TOPLEVEL_WIDTH,
98
99
	PROP_TOPLEVEL_HEIGHT,
	PROP_SUPPORT_WARNING
100
101
};

Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
102
103
104
static guint         glade_widget_signals[LAST_SIGNAL] = {0};
static GQuark        glade_widget_name_quark = 0;

105

106
G_DEFINE_TYPE (GladeWidget, glade_widget, G_TYPE_INITIALLY_UNOWNED)
107

108
109
110
/*******************************************************************************
                           GladeWidget class methods
 *******************************************************************************/
111
112
113
114
115
116
117
118
119
120
static void
glade_widget_set_packing_actions (GladeWidget *widget, GladeWidget *parent)
{
	if (widget->packing_actions)
	{
		g_list_foreach (widget->packing_actions, (GFunc)g_object_unref, NULL);
		g_list_free (widget->packing_actions);
		widget->packing_actions = NULL;
	}
	
121
122
	if (parent->adaptor->packing_actions)
		widget->packing_actions = glade_widget_adaptor_pack_actions_new (parent->adaptor);
123
124
}

125
static void
126
glade_widget_add_child_impl (GladeWidget  *widget,
127
128
			     GladeWidget  *child,
			     gboolean      at_mouse)
129
{
130
131
	g_object_ref (child);

Tristan Van Berkom's avatar
Tristan Van Berkom committed
132
133
134
	/* Safe to set the parent first... setting it afterwards
	 * creates packing properties, and that is not always
	 * desirable.
135
136
137
	 */
	glade_widget_set_parent (child, widget);

138
139
140
141
	/* Set packing actions first so we have access from the plugin
	 */
	glade_widget_set_packing_actions (child, widget);

142
143
	glade_widget_adaptor_add 
		(widget->adaptor, widget->object, child->object);
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
	/* XXX FIXME:
	 * We have a fundamental flaw here, we set packing props
	 * after parenting the widget so that we can introspect the
	 * values setup by the runtime widget, in which case the plugin
	 * cannot access its packing properties and set them sensitive
	 * or connect to thier signals etc. maybe its not so important
	 * but its a flaw worthy of note, some kind of double pass api
	 * would be needed to accomadate this.
	 */

	
	/* Setup packing properties here so we can introspect the new
	 * values from the backend.
	 */
159
	glade_widget_set_packing_properties (child, widget);
160
}
161

162
static void
163
164
glade_widget_remove_child_impl (GladeWidget  *widget,
				GladeWidget  *child)
165
{
166
167
	glade_widget_adaptor_remove
		(widget->adaptor, widget->object, child->object);
168

169
	child->parent = NULL;
170
171

	g_object_unref (child);
172
}
173

Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
174
175
176
177
178
179
180
181
static void
glade_widget_replace_child_impl (GladeWidget *widget,
				 GObject     *old_object,
				 GObject     *new_object)
{
	GladeWidget *gnew_widget = glade_widget_get_from_gobject (new_object);
	GladeWidget *gold_widget = glade_widget_get_from_gobject (old_object);

182
183
	if (gnew_widget)
	{
184
185
		g_object_ref (gnew_widget);

186
187
188
189
190
191
192
		gnew_widget->parent = widget;

		/* Set packing actions first so we have access from the plugin
		 */
		glade_widget_set_packing_actions (gnew_widget, widget);
	}

193
194
195
196
197
198
199
	if (gold_widget)
	{ 
		g_object_unref (gold_widget);

		if (gold_widget != gnew_widget)
			gold_widget->parent = NULL;
	}
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
200

201
202
	glade_widget_adaptor_replace_child 
		(widget->adaptor, widget->object,
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
203
204
		 old_object, new_object);

205
206
	/* Setup packing properties here so we can introspect the new
	 * values from the backend.
207
	 */
208
	if (gnew_widget)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
209
210
211
		glade_widget_set_packing_properties (gnew_widget, widget);
}

212
213
214
215
216
static void
glade_widget_add_signal_handler_impl (GladeWidget *widget, GladeSignal *signal_handler)
{
	GPtrArray *signals;
	GladeSignal *new_signal_handler;
217

218
219
220
221
222
223
224
225
	g_return_if_fail (GLADE_IS_WIDGET (widget));
	g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));

	signals = glade_widget_list_signal_handlers (widget, signal_handler->name);
	if (!signals)
	{
		signals = g_ptr_array_new ();
		g_hash_table_insert (widget->signals, g_strdup (signal_handler->name), signals);
226
227
	}

228
229
	new_signal_handler = glade_signal_clone (signal_handler);
	g_ptr_array_add (signals, new_signal_handler);
230
231

	glade_project_update_signal_support_warning (widget, new_signal_handler);
232
233
}

234
static void
235
glade_widget_remove_signal_handler_impl (GladeWidget *widget, GladeSignal *signal_handler)
236
{
237
238
239
	GPtrArray   *signals;
	GladeSignal *tmp_signal_handler;
	guint        i;
240

241
242
	g_return_if_fail (GLADE_IS_WIDGET (widget));
	g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
243

244
	signals = glade_widget_list_signal_handlers (widget, signal_handler->name);
245

246
247
	/* trying to remove an inexistent signal? */
	g_assert (signals);
248

249
250
251
252
253
254
255
256
257
258
259
	for (i = 0; i < signals->len; i++)
	{
		tmp_signal_handler = g_ptr_array_index (signals, i);
		if (glade_signal_equal (tmp_signal_handler, signal_handler))
		{
			glade_signal_free (tmp_signal_handler);
			g_ptr_array_remove_index (signals, i);
			break;
		}
	}
}
260

261
262
263
264
265
266
static void
glade_widget_change_signal_handler_impl (GladeWidget *widget,
					 GladeSignal *old_signal_handler,
					 GladeSignal *new_signal_handler)
{
	GPtrArray   *signals;
267
	GladeSignal *signal_handler_iter;
268
269
270
271
272
273
	guint        i;
	
	g_return_if_fail (GLADE_IS_WIDGET (widget));
	g_return_if_fail (GLADE_IS_SIGNAL (old_signal_handler));
	g_return_if_fail (GLADE_IS_SIGNAL (new_signal_handler));
	g_return_if_fail (strcmp (old_signal_handler->name, new_signal_handler->name) == 0);
274

275
	signals = glade_widget_list_signal_handlers (widget, old_signal_handler->name);
276

277
278
	/* trying to remove an inexistent signal? */
	g_assert (signals);
279

280
281
	for (i = 0; i < signals->len; i++)
	{
282
283
		signal_handler_iter = g_ptr_array_index (signals, i);
		if (glade_signal_equal (signal_handler_iter, old_signal_handler))
284
285
286
287
		{
			if (strcmp (old_signal_handler->handler,
				    new_signal_handler->handler) != 0)
			{
288
289
				g_free (signal_handler_iter->handler);
				signal_handler_iter->handler =
290
291
					g_strdup (new_signal_handler->handler);
			}
Tristan Van Berkom's avatar
Tristan Van Berkom committed
292

293
			/* Handler */
294
295
296
			if (signal_handler_iter->handler)
				g_free (signal_handler_iter->handler);
			signal_handler_iter->handler =
297
298
299
				g_strdup (new_signal_handler->handler);
			
			/* Object */
300
301
302
			if (signal_handler_iter->userdata)
				g_free (signal_handler_iter->userdata);
			signal_handler_iter->userdata = 
303
304
				g_strdup (new_signal_handler->userdata);
			
305
306
			signal_handler_iter->after    = new_signal_handler->after;
			signal_handler_iter->swapped  = new_signal_handler->swapped;
307
308
309
310
			break;
		}
	}
}
Tristan Van Berkom's avatar
Tristan Van Berkom committed
311
312


313
static gboolean
314
315
glade_widget_button_press_event_impl (GladeWidget    *gwidget,
				      GdkEvent       *base_event)
316
{
317
	GtkWidget         *widget;
318
	GdkEventButton    *event = (GdkEventButton *)base_event;
319
	gboolean           handled = FALSE;
320

321
	/* make sure to grab focus, since we may stop default handlers */
322
	widget = GTK_WIDGET (glade_widget_get_object (gwidget));
323
	if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
324
		gtk_widget_grab_focus (widget);
325

326
327
	/* if it's already selected don't stop default handlers, e.g. toggle button */
	if (event->button == 1)
328
	{
329
330
		if (event->state & GDK_CONTROL_MASK)
		{
331
332
			if (glade_project_is_selected (gwidget->project,
						       gwidget->object))
333
				glade_app_selection_remove 
334
					(gwidget->object, TRUE);
335
336
			else
				glade_app_selection_add
337
					(gwidget->object, TRUE);
338
339
			handled = TRUE;
		}
340
341
		else if (glade_project_is_selected (gwidget->project,
						    gwidget->object) == FALSE)
342
343
344
		{
			glade_util_clear_selection ();
			glade_app_selection_set 
345
				(gwidget->object, TRUE);
346
347
348
349
350
351

			/* Add selection without interrupting event flow 
			 * when shift is down, this allows better behaviour
			 * for GladeFixed children 
			 */
			handled = !(event->state & GDK_SHIFT_MASK);
352
353
		}
	}
354
355

	/* Give some kind of access in case of missing right button */
356
	if (!handled && glade_popup_is_popup_event (event))
357
358
359
       	{
			glade_popup_widget_pop (gwidget, event, TRUE);
			handled = TRUE;
360
361
	}

362
363
	return handled;
}
364

365
static gboolean
366
367
glade_widget_event_impl (GladeWidget *gwidget,
			 GdkEvent    *event)
368
{
369
370
	gboolean handled = FALSE;

371
	g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
372
373
374
375

	switch (event->type) 
	{
	case GDK_BUTTON_PRESS:
376
377
378
379
380
381
382
383
384
385
386
387
388
389
		g_signal_emit (gwidget, 
			       glade_widget_signals[BUTTON_PRESS_EVENT], 0, 
			       event, &handled);
		break;
	case GDK_BUTTON_RELEASE:
		g_signal_emit (gwidget, 
			       glade_widget_signals[BUTTON_RELEASE_EVENT], 0, 
			       event, &handled);
		break;
	case GDK_MOTION_NOTIFY:
		g_signal_emit (gwidget, 
			       glade_widget_signals[MOTION_NOTIFY_EVENT], 0, 
			       event, &handled);
		break;
390
391
392
393
	default:
		break;
	}

394
	return handled;
395
396
}

397
398
399
400
401
402
403
404

/**
 * glade_widget_event:
 * @event: A #GdkEvent
 *
 * Feed an event to be handled on the project GladeWidget
 * hierarchy.
 *
405
 * Returns: whether the event was handled or not.
406
407
408
409
 */
gboolean
glade_widget_event (GladeWidget *gwidget,
		    GdkEvent    *event)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
410
{
411
	gboolean   handled = FALSE;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
412

413
	/* Lets just avoid some synthetic events (like focus-change) */
414
	if (((GdkEventAny *)event)->window == NULL) return FALSE;
415

416
	handled = GLADE_WIDGET_GET_CLASS (gwidget)->event (gwidget, event);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
417

418
#if 0
419
420
421
	if (event->type != GDK_EXPOSE)
		g_print ("event widget '%s' handled '%d' event '%d'\n",
			 gwidget->name, handled, event->type);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
422
#endif
423

424
	return handled;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
425
426
}

427
428
429
/*******************************************************************************
                      GObjectClass & Object Construction
 *******************************************************************************/
430
431
432

/*
 * This function creates new GObject parameters based on the GType of the 
433
 * GladeWidgetAdaptor and its default values.
434
435
436
437
438
439
440
441
442
 *
 * If a GladeWidget is specified, it will be used to apply the
 * values currently in use.
 */
static GParameter *
glade_widget_template_params (GladeWidget      *widget,
			      gboolean          construct,
			      guint            *n_params)
{
443
	GladeWidgetAdaptor    *klass;
444
445
446
447
448
449
450
451
452
453
	GArray              *params;
	GObjectClass        *oclass;
	GParamSpec         **pspec;
	GladeProperty       *glade_property;
	GladePropertyClass  *pclass;
	guint                n_props, i;

	g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
	g_return_val_if_fail (n_params != NULL, NULL);

454
	klass = widget->adaptor;
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
	
	/* As a slight optimization, we never unref the class
	 */
	oclass = g_type_class_ref (klass->type);
	pspec  = g_object_class_list_properties (oclass, &n_props);
	params = g_array_new (FALSE, FALSE, sizeof (GParameter));

	for (i = 0; i < n_props; i++)
	{
		GParameter parameter = { 0, };

		if ((glade_property = 
		     glade_widget_get_property (widget, pspec[i]->name)) == NULL)
			continue;

470
		pclass = glade_property->klass;
471
472
473
		
		/* Ignore properties based on some criteria
		 */
474
475
		if (!glade_property_get_enabled (glade_property) ||
		    pclass == NULL  || /* Unaccounted for in the builder */
476
		    pclass->virt    || /* should not be set before 
477
478
					  GladeWidget wrapper exists */
		    pclass->ignore)    /* Catalog explicitly ignores the object */
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
			continue;

		if (construct &&
		    (pspec[i]->flags & 
		     (G_PARAM_CONSTRUCT|G_PARAM_CONSTRUCT_ONLY)) == 0)
			continue;
		else if (!construct &&
			 (pspec[i]->flags & 
			  (G_PARAM_CONSTRUCT|G_PARAM_CONSTRUCT_ONLY)) != 0)
			continue;

		if (g_value_type_compatible (G_VALUE_TYPE (pclass->def),
					     pspec[i]->value_type) == FALSE)
		{
			g_critical ("Type mismatch on %s property of %s",
				    parameter.name, klass->name);
			continue;
		}

		if (g_param_values_cmp (pspec[i], 
					glade_property->value, 
					pclass->orig_def) == 0)
			continue;


		parameter.name = pspec[i]->name; /* These are not copied/freed */
		g_value_init (&parameter.value, pspec[i]->value_type);
		g_value_copy (glade_property->value, &parameter.value);
		
		g_array_append_val (params, parameter);
	}
	g_free (pspec);

	*n_params = params->len;
	return (GParameter *)g_array_free (params, FALSE);
}

static void
free_params (GParameter *params, guint n_params)
{
	gint i;
	for (i = 0; i < n_params; i++)
		g_value_unset (&(params[i].value));
	g_free (params);

}

static GObject *
527
528
glade_widget_build_object (GladeWidget *widget,
			   GladeWidget *template,
529
			   GladeCreateReason reason)
530
531
532
{
	GParameter          *params;
	GObject             *object;
533
	guint                n_params, i;
534
535
	
	if (reason == GLADE_CREATE_LOAD)
536
537
	{
		object = glade_widget_adaptor_construct_object (widget->adaptor, 0, NULL);
538
		glade_widget_set_object (widget, object, TRUE);
539
540
		return object;
	}
541

542
	if (template)
543
		params = glade_widget_template_params (widget, TRUE, &n_params);
544
	else
545
		params = glade_widget_adaptor_default_params (widget->adaptor, TRUE, &n_params);
546
547
548

	/* Create the new object with the correct parameters.
	 */
549
	object = glade_widget_adaptor_construct_object (widget->adaptor, n_params, params);
550
551
552

	free_params (params, n_params);

553
554
	/* Dont destroy toplevels when rebuilding, handle that separately */
	glade_widget_set_object (widget, object, reason != GLADE_CREATE_REBUILD);
555
556

	if (template)
557
		params = glade_widget_template_params (widget, FALSE, &n_params);
558
	else
559
		params = glade_widget_adaptor_default_params (widget->adaptor, FALSE, &n_params);
560
561

	for (i = 0; i < n_params; i++)
562
		glade_widget_adaptor_set_property (widget->adaptor, object, params[i].name, &(params[i].value));
563
564
565

	free_params (params, n_params);

566
567
568
	return object;
}

569
570
/**
 * glade_widget_dup_properties:
571
 * @dest_widget: the widget we are copying properties for
572
573
 * @template_props: the #GladeProperty list to copy
 * @as_load: whether to behave as if loading the project
574
575
 * @copy_parentless: whether to copy reffed widgets at all
 * @exact: whether to copy reffed widgets exactly
576
577
578
579
580
581
 *
 * Copies a list of properties, if @as_load is specified, then
 * properties that are not saved to the glade file are ignored.
 *
 * Returns: A newly allocated #GList of new #GladeProperty objects.
 */
582
GList *
583
glade_widget_dup_properties (GladeWidget *dest_widget, GList *template_props, gboolean as_load, 
584
			     gboolean copy_parentless, gboolean exact)
585
586
587
588
589
590
{
	GList *list, *properties = NULL;

	for (list = template_props; list && list->data; list = list->next)
	{
		GladeProperty *prop = list->data;
591
		
592
		if (prop->klass->save == FALSE && as_load)
593
594
			continue;

595
596
597
598
599
600
601

		if (prop->klass->parentless_widget && copy_parentless)
		{
			GObject *object = NULL;
			GladeWidget *parentless;

			glade_property_get (prop, &object);
602
603
604

			prop = glade_property_dup (prop, NULL);

605
606
607
608
609
610
			if (object)
			{
				parentless = glade_widget_get_from_gobject (object);

				parentless = glade_widget_dup (parentless, exact);

611
612
				glade_widget_set_project (parentless, dest_widget->project);

613
614
615
616
617
618
619
620
				glade_property_set (prop, parentless->object);
			}
		} 
		else 
			prop = glade_property_dup (prop, NULL);


		properties = g_list_prepend (properties, prop);
621
622
623
624
	}
	return g_list_reverse (properties);
}

625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
/**
 * glade_widget_remove_property:
 * @widget: A #GladeWidget
 * @id_property: the name of the property
 *
 * Removes the #GladeProperty indicated by @id_property
 * from @widget (this is intended for use in the plugin, to
 * remove properties from composite children that dont make
 * sence to allow the user to specify, notably - properties
 * that are proxied through the composite widget's properties or
 * style properties).
 */
void
glade_widget_remove_property (GladeWidget  *widget,
			      const gchar  *id_property)
{
	GladeProperty *prop;

	g_return_if_fail (GLADE_IS_WIDGET (widget));
	g_return_if_fail (id_property);

646
647
648
649
	/* XXX FIXME: currently we arent calling this on packing properties,
	 * but doing so could cause crashes because the hash table is not
	 * managed properly
	 */
650
651
652
	if ((prop = glade_widget_get_property (widget, id_property)) != NULL)
	{
		widget->properties = g_list_remove (widget->properties, prop);
653
		g_hash_table_remove (widget->props_hash, prop->klass->id);
654
655
656
657
658
659
660
		g_object_unref (prop);
	}
	else
		g_critical ("Couldnt find property %s on widget %s\n",
			    id_property, widget->name);
}

661
662
663
664
665
666
667
668
669
670
671
672
673
674
static void
glade_widget_set_catalog_defaults (GList *list)
{
	GList *l;
	for (l = list; l && l->data; l = l->next)
	{
		GladeProperty *prop  = l->data;
		GladePropertyClass *klass = prop->klass;
		
		if (glade_property_equals_value (prop, klass->orig_def) &&
		     g_param_values_cmp (klass->pspec, klass->orig_def, klass->def))
			glade_property_reset (prop);
	}
}
675

676
677
678
679
680
681
682
static void
glade_widget_sync_custom_props (GladeWidget *widget)
{
	GList *l;
	for (l = widget->properties; l && l->data; l = l->next)
	{
		GladeProperty *prop  = GLADE_PROPERTY(l->data);
683

684
		if (prop->klass->virt || prop->klass->needs_sync)
685
686
			glade_property_sync (prop);

687
688
689
690
691
692
693
694
695
696
697
698
699
	}
}

static void
glade_widget_sync_packing_props (GladeWidget *widget)
{
	GList *l;
	for (l = widget->packing_properties; l && l->data; l = l->next) {
		GladeProperty *prop  = GLADE_PROPERTY(l->data);
		glade_property_sync (prop);
	}
}

700

701
702
703
704
705
706
707
708
709
static GObject *
glade_widget_constructor (GType                  type,
			  guint                  n_construct_properties,
			  GObjectConstructParam *construct_properties)
{
	GladeWidget      *gwidget;
	GObject          *ret_obj, *object;
	GList            *properties = NULL, *list;

710
	ret_obj = G_OBJECT_CLASS (glade_widget_parent_class)->constructor
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
		(type, n_construct_properties, construct_properties);

	gwidget = GLADE_WIDGET (ret_obj);

	if (gwidget->name == NULL)
	{
		if (gwidget->internal)
		{
			gchar *name_base = g_strdup_printf ("%s-%s", 
							    gwidget->construct_internal, 
							    gwidget->internal);
			
			if (gwidget->project)
			{
				gwidget->name = 
					glade_project_new_widget_name (gwidget->project, 
727
								       gwidget,
728
729
730
731
732
733
734
735
736
								       name_base);
				g_free (name_base);
			}
			else
				gwidget->name = name_base;

		}
		else if (gwidget->project)
			gwidget->name = glade_project_new_widget_name
737
				(gwidget->project, gwidget,
738
				 gwidget->adaptor->generic_name);
739
740
		else
			gwidget->name = 
741
				g_strdup (gwidget->adaptor->generic_name);
742
743
744
745
746
	}

	if (gwidget->construct_template)
	{
		properties = glade_widget_dup_properties
747
			(gwidget, gwidget->construct_template->properties, FALSE, TRUE, gwidget->construct_exact);
748
749
750
751
752
753
		
		glade_widget_set_properties (gwidget, properties);
	}

	if (gwidget->object == NULL)
	{
754
755
756
		object = glade_widget_build_object (gwidget, 
						    gwidget->construct_template, 
						    gwidget->construct_reason);
757
758
	}

759
760
761
762
763
764
765
766
767
768
769
770
	/* Copy sync parentless widget props here after a dup
	 */
	if (gwidget->construct_reason == GLADE_CREATE_COPY)
	{
		for (list = gwidget->properties; list; list = list->next)
		{
			GladeProperty *property = list->data;
			if (property->klass->parentless_widget)
				glade_property_sync (property);
		}
	}

771
772
773
774
	/* Setup width/height */
	gwidget->width  = GWA_DEFAULT_WIDTH (gwidget->adaptor);
	gwidget->height = GWA_DEFAULT_HEIGHT (gwidget->adaptor);

775
776
777
778
	/* Introspect object properties before passing it to post_create,
	 * but only when its freshly created (depend on glade file at
	 * load time and copying properties at dup time).
	 */
779
	if (gwidget->construct_reason == GLADE_CREATE_USER)
780
781
		for (list = gwidget->properties; list; list = list->next)
			glade_property_load (GLADE_PROPERTY (list->data));
782
	
783
784
785
786
	/* We only use catalog defaults when the widget was created by the user! */
	if (gwidget->construct_reason == GLADE_CREATE_USER)
		glade_widget_set_catalog_defaults (gwidget->properties);
	
787
788
789
	/* Only call this once the GladeWidget is completely built
	 * (but before calling custom handlers...)
	 */
790
791
792
	glade_widget_adaptor_post_create (gwidget->adaptor, 
					  gwidget->object,
					  gwidget->construct_reason);
793

794
	/* Virtual properties need to be explicitly synchronized.
795
796
797
798
799
800
	 */
	if (gwidget->construct_reason == GLADE_CREATE_USER)
		glade_widget_sync_custom_props (gwidget);

	if (gwidget->parent && gwidget->packing_properties == NULL)
		glade_widget_set_packing_properties (gwidget, gwidget->parent);
801
	
802
	if (GTK_IS_WIDGET (gwidget->object) && !gtk_widget_is_toplevel (GTK_WIDGET (gwidget->object)))
Tristan Van Berkom's avatar
Tristan Van Berkom committed
803
804
805
806
807
808
809
	{
		gwidget->visible = TRUE;
		gtk_widget_show_all (GTK_WIDGET (gwidget->object));
	}
	else if (GTK_IS_WIDGET (gwidget->object) == FALSE)
		gwidget->visible = TRUE;
	
810
811
812
	return ret_obj;
}

813
814
static void
glade_widget_finalize (GObject *object)
815
{
816
	GladeWidget *widget = GLADE_WIDGET (object);
817

818
	g_return_if_fail (GLADE_IS_WIDGET (object));
819

820
821
822
823
824
#if 0
	/* A good way to check if refcounts are balancing at project close time */
	g_print ("Finalizing widget %s\n", widget->name);
#endif

825
826
	g_free (widget->name);
	g_free (widget->internal);
827
	g_free (widget->support_warning);
828
	g_hash_table_destroy (widget->signals);
829

830
831
832
833
834
	if (widget->props_hash)
		g_hash_table_destroy (widget->props_hash);
	if (widget->pack_props_hash)
		g_hash_table_destroy (widget->pack_props_hash);

835
836
837
838
839
840
841
842
843
844
	G_OBJECT_CLASS (glade_widget_parent_class)->finalize(object);
}

static void
reset_object_property (GladeProperty *property,
		       GladeProject  *project)
{
	if (glade_property_class_is_object (property->klass, 
					    glade_project_get_format (project)))
		glade_property_reset (property);
845
846
}

847
848
static void
glade_widget_dispose (GObject *object)
849
{
850
	GladeWidget *widget = GLADE_WIDGET (object);
851

852
	glade_widget_push_superuser ();
853

854
855
	/* Release references by way of object properties... */
	while (widget->prop_refs)
856
	{
857
858
		GladeProperty *property = GLADE_PROPERTY (widget->prop_refs->data);
		glade_property_set (property, NULL);
859
	}
860

861
862
863
	if (widget->properties)
		g_list_foreach (widget->properties, (GFunc)reset_object_property, widget->project);

864
865
	/* We have to make sure properties release thier references on other widgets first 
	 * hence the reset (for object properties) */
866
867
868
869
	if (widget->properties)
	{
		g_list_foreach (widget->properties, (GFunc)g_object_unref, NULL);
		g_list_free (widget->properties);
870
		widget->properties = NULL;
871
	}
872

873
	glade_widget_set_object (widget, NULL, TRUE);
874
875
876
877
878
	
	if (widget->packing_properties)
	{
		g_list_foreach (widget->packing_properties, (GFunc)g_object_unref, NULL);
		g_list_free (widget->packing_properties);
879
		widget->packing_properties = NULL;
880
	}
881
882
883
884
885
	
	if (widget->actions)
	{
		g_list_foreach (widget->actions, (GFunc)g_object_unref, NULL);
		g_list_free (widget->actions);
886
		widget->actions = NULL;
887
	}
888
889
890
891
892
	
	if (widget->packing_actions)
	{
		g_list_foreach (widget->packing_actions, (GFunc)g_object_unref, NULL);
		g_list_free (widget->packing_actions);
893
		widget->packing_actions = NULL;
894
	}
895

896
897
	glade_widget_pop_superuser ();

898
	G_OBJECT_CLASS (glade_widget_parent_class)->dispose (object);
899
900
}

901
static void
902
903
904
905
glade_widget_set_real_property (GObject         *object,
				guint            prop_id,
				const GValue    *value,
				GParamSpec      *pspec)
906
{
907
	GladeWidget *widget;
908

909
	widget = GLADE_WIDGET (object);
910

911
	switch (prop_id)
912
	{
913
914
915
916
917
918
919
920
921
922
	case PROP_NAME:
		glade_widget_set_name (widget, g_value_get_string (value));
		break;
	case PROP_INTERNAL:
		glade_widget_set_internal (widget, g_value_get_string (value));
		break;
	case PROP_ANARCHIST:
		widget->anarchist = g_value_get_boolean (value);
		break;
	case PROP_OBJECT:
923
		if (g_value_get_object (value))
924
			glade_widget_set_object (widget, g_value_get_object (value), TRUE);
925
926
927
928
		break;
	case PROP_PROJECT:
		glade_widget_set_project (widget, GLADE_PROJECT (g_value_get_object (value)));
		break;
929
930
931
	case PROP_ADAPTOR:
		glade_widget_set_adaptor (widget, GLADE_WIDGET_ADAPTOR
					  (g_value_get_object (value)));
932
933
934
935
936
937
938
		break;
	case PROP_PROPERTIES:
		glade_widget_set_properties (widget, (GList *)g_value_get_pointer (value));
		break;
	case PROP_PARENT:
		glade_widget_set_parent (widget, GLADE_WIDGET (g_value_get_object (value)));
		break;
939
940
941
942
943
944
945
	case PROP_INTERNAL_NAME:
		if (g_value_get_string (value))
			widget->construct_internal = g_value_dup_string (value);
		break;
	case PROP_TEMPLATE:
		widget->construct_template = g_value_get_object (value);
		break;
946
947
948
	case PROP_TEMPLATE_EXACT:
		widget->construct_exact = g_value_get_boolean (value);
		break;
949
950
951
	case PROP_REASON:
		widget->construct_reason = g_value_get_int (value);
		break;
952
953
954
955
956
957
	case PROP_TOPLEVEL_WIDTH:
		widget->width = g_value_get_int (value);
		break;
	case PROP_TOPLEVEL_HEIGHT:
		widget->height = g_value_get_int (value);
		break;
958
959
960
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
961
962
963
964
	}
}

static void
965
966
967
968
glade_widget_get_real_property (GObject         *object,
				guint            prop_id,
				GValue          *value,
				GParamSpec      *pspec)
969
{
970
971
972
973
974
	GladeWidget *widget;

	widget = GLADE_WIDGET (object);

	switch (prop_id)
975
	{
976
977
978
979
980
981
982
983
984
	case PROP_NAME:
		g_value_set_string (value, widget->name);
		break;
	case PROP_INTERNAL:
		g_value_set_string (value, widget->internal);
		break;
	case PROP_ANARCHIST:
		g_value_set_boolean (value, widget->anarchist);
		break;
985
986
	case PROP_ADAPTOR:
		g_value_set_object (value, widget->adaptor);
987
988
989
990
991
992
993
994
995
996
997
998
999
		break;
	case PROP_PROJECT:
		g_value_set_object (value, G_OBJECT (widget->project));
		break;
	case PROP_OBJECT:
		g_value_set_object (value, widget->object);
		break;
	case PROP_PROPERTIES:
		g_value_set_pointer (value, widget->properties);
		break;
	case PROP_PARENT:
		g_value_set_object (value, widget->parent);
		break;
1000
1001
1002
1003
1004
1005
	case PROP_TOPLEVEL_WIDTH:
		g_value_set_int (value, widget->width);
		break;
	case PROP_TOPLEVEL_HEIGHT:
		g_value_set_int (value, widget->height);
		break;
1006
1007
1008
	case PROP_SUPPORT_WARNING:
		g_value_set_string (value, widget->support_warning);
		break;
1009
1010
1011
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
1012
1013
1014
1015
	}
}

static void
1016
free_signals (gpointer value)
1017
{
1018
1019
1020
	GPtrArray *signals = (GPtrArray*) value;
	guint i;
	guint nb_signals;
1021

1022
1023
	if (signals == NULL)
		return;
1024

1025
1026
1027
1028
1029
	/* g_ptr_array_foreach (signals, (GFunc) glade_signal_free, NULL);
	 * only available in modern versions of Gtk+ */
	nb_signals = signals->len;
	for (i = 0; i < nb_signals; i++)
		glade_signal_free (g_ptr_array_index (signals, i));
1030

1031
	g_ptr_array_free (signals, TRUE);
1032
1033
}

1034
static void
1035
glade_widget_init (GladeWidget *widget)
1036
{
1037
	widget->adaptor = NULL;
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
	widget->project = NULL;
	widget->name = NULL;
	widget->internal = NULL;
	widget->object = NULL;
	widget->properties = NULL;
	widget->packing_properties = NULL;
	widget->prop_refs = NULL;
	widget->signals = g_hash_table_new_full
		(g_str_hash, g_str_equal,
		 (GDestroyNotify) g_free,
		 (GDestroyNotify) free_signals);
1049
1050
1051
1052
	
	/* Initial invalid values */
	widget->width  = -1;
	widget->height = -1;
1053
1054
}

1055
static void
1056
glade_widget_class_init (GladeWidgetClass *klass)
1057
{
1058
	GObjectClass *object_class;
1059

Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
1060
1061
1062
1063
	if (glade_widget_name_quark == 0)
		glade_widget_name_quark = 
			g_quark_from_static_string ("GladeWidgetDataTag");

1064
	object_class = G_OBJECT_CLASS (klass);
1065

1066
	object_class->constructor     = glade_widget_constructor;
1067
1068
1069
1070
	object_class->finalize        = glade_widget_finalize;
	object_class->dispose         = glade_widget_dispose;
	object_class->set_property    = glade_widget_set_real_property;
	object_class->get_property    = glade_widget_get_real_property;
1071

1072
1073
	klass->add_child              = glade_widget_add_child_impl;
	klass->remove_child           = glade_widget_remove_child_impl;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
1074
	klass->replace_child          = glade_widget_replace_child_impl;
1075
	klass->event                  = glade_widget_event_impl;
1076

1077
1078
1079
	klass->add_signal_handler     = glade_widget_add_signal_handler_impl;
	klass->remove_signal_handler  = glade_widget_remove_signal_handler_impl;
	klass->change_signal_handler  = glade_widget_change_signal_handler_impl;
1080

1081
1082
1083
	klass->button_press_event     = glade_widget_button_press_event_impl;
	klass->button_release_event   = NULL;
	klass->motion_notify_event    = NULL;
1084

1085
1086
1087
1088
1089
1090
1091
	g_object_class_install_property
		(object_class, PROP_NAME,
		 g_param_spec_string ("name", _("Name"),
				      _("The name of the widget"),
				      NULL,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT));
1092

1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
	g_object_class_install_property
		(object_class, PROP_INTERNAL,
		 g_param_spec_string ("internal", _("Internal name"),
				      _("The internal name of the widget"),
				      NULL, G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT));
	
	g_object_class_install_property
		(object_class, PROP_ANARCHIST,
		 g_param_spec_boolean ("anarchist", _("Anarchist"),
				       _("Whether this composite child is "
					 "an ancestral child or an anarchist child"),
				       FALSE, G_PARAM_READWRITE |
				       G_PARAM_CONSTRUCT_ONLY));
1107

1108
1109
1110
1111
1112
1113
1114
	g_object_class_install_property
		(object_class, PROP_OBJECT,
		 g_param_spec_object ("object", _("Object"),
				      _("The object associated"),
				      G_TYPE_OBJECT,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT));
1115

1116
	g_object_class_install_property
1117
1118
1119
1120
1121
1122
		(object_class, PROP_ADAPTOR,
		   g_param_spec_object ("adaptor", _("Adaptor"),
					_("The class adaptor for the associated widget"),
					GLADE_TYPE_WIDGET_ADAPTOR,
					G_PARAM_READWRITE |
					G_PARAM_CONSTRUCT_ONLY));
1123

1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
	g_object_class_install_property
		(object_class, PROP_PROJECT,
		 g_param_spec_object ("project", _("Project"),
				      _("The glade project that "
					"this widget belongs to"),
				      GLADE_TYPE_PROJECT,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT));

	g_object_class_install_property
		(object_class, PROP_PROPERTIES,
		 g_param_spec_pointer ("properties", _("Properties"),
				       _("A list of GladeProperties"),
				       G_PARAM_READWRITE |
				       G_PARAM_CONSTRUCT_ONLY));
1139

1140
1141
1142
	g_object_class_install_property
		(object_class, 	PROP_PARENT,
		 g_param_spec_object ("parent", _("Parent"),
1143
1144
1145
1146
				      _("A pointer to the parenting GladeWidget"),
				      GLADE_TYPE_WIDGET,
				      G_PARAM_READWRITE |
				      G_PARAM_CONSTRUCT));
1147

1148
1149
1150
1151
1152
	g_object_class_install_property
		(ob