glade-widget.c 135 KB
Newer Older
Jose Maria Celorio's avatar
Jose Maria Celorio committed
1
/*
2
 * Copyright (C) 2008 Tristan Van Berkom
3
4
 * Copyright (C) 2004 Joaquin Cuenca Abela
 * Copyright (C) 2001, 2002, 2003 Ximian, Inc.
Jose Maria Celorio's avatar
Jose Maria Celorio committed
5
6
7
8
9
10
11
12
13
14
15
16
17
 *
 * 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
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Jose Maria Celorio's avatar
Jose Maria Celorio committed
19
20
 *
 * Authors:
21
 *   Joaquin Cuenca Abela <e98cuenc@yahoo.com>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
22
 *   Chema Celorio <chema@celorio.com>
23
 *   Tristan Van Berkom <tvb@gnome.org>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
24
25
 */

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

30
31
32
33
34
35
36
37
38
/**
 * 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
39
#include <string.h>
40
#include <glib-object.h>
41
42
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n-lib.h>
Jose Maria Celorio's avatar
Jose Maria Celorio committed
43
#include "glade.h"
44
#include "glade-accumulators.h"
Jose Maria Celorio's avatar
Jose Maria Celorio committed
45
#include "glade-project.h"
46
#include "glade-widget-adaptor.h"
47
48
#include "glade-widget.h"
#include "glade-marshallers.h"
Jose Maria Celorio's avatar
Jose Maria Celorio committed
49
50
#include "glade-property.h"
#include "glade-property-class.h"
51
#include "glade-placeholder.h"
52
#include "glade-signal.h"
53
#include "glade-popup.h"
54
#include "glade-editor.h"
55
#include "glade-app.h"
56
#include "glade-design-view.h"
57
#include "glade-widget-action.h"
58
#include "glade-signal-model.h"
Jose Maria Celorio's avatar
Jose Maria Celorio committed
59

60

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

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148

struct _GladeWidgetPrivate {

  GladeWidgetAdaptor *adaptor; /* An adaptor class for the object type */

  GladeProject       *project; /* A pointer to the project that this 
				  widget currently belongs to. */

  GladeWidget  *parent;  /* A pointer to the parent widget in the hierarchy */
	
  gchar *name; /* The name of the widget. For example window1 or
		* button2. This is a unique name and is the one
		* used when loading widget with libglade
		*/

  gchar *support_warning; /* A warning message for version incompatabilities
			   * in this widget
			   */

  gchar *internal; /* If the widget is an internal child of 
		    * another widget this is the name of the 
		    * internal child, otherwise is NULL.
		    * Internal children cannot be deleted.
		    */

  gboolean anarchist; /* Some composite widgets have internal children
		       * that are not part of the same hierarchy; hence 'anarchists',
		       * typicly a popup window or its child (we need to mark
		       * them so we can avoid bookkeeping packing props on them etc.).
		       */

  GObject *object; /* A pointer to the object that was created.
		    * if it is a GtkWidget; it is shown as a "view"
		    * of the GladeWidget. This object is updated as
		    * the properties are modified for the GladeWidget.
		    */

  GList *properties; /* A list of GladeProperty. A GladeProperty is an
		      * instance of a GladePropertyClass. If a
		      * GladePropertyClass for a gtkbutton is label, its
		      * property is "Ok". 
		      */

  GList *packing_properties; /* A list of GladeProperty. Note that these
			      * properties are related to the container
			      * of the widget, thus they change after
			      * pasting the widget to a different
			      * container. Toplevels widget do not have
			      * packing properties.
			      * See also child_properties of 
			      * GladeWidgetClass.
			      */

  GHashTable *props_hash; /* A Quick reference table to speed up calls to glade_widget_get_property()
			   */	
  GHashTable *pack_props_hash; /* A Quick reference table to speed up calls to glade_widget_get_pack_property()
				*/

  GHashTable *signals; /* A table with a GPtrArray of GladeSignals (signal handlers),
			* indexed by its name */

  GList     *prop_refs; /* List of properties in the project who's value are `this object'
			 * (this is used to set/unset those properties when the object is
			 * added/removed from the project).
			 */

  gint               width;   /* Current size used in the UI, this is only */
  gint               height;  /* usefull for parentless widgets in the
			       * GladeDesignLayout */

  GList *actions;		/* A GladeWidgetAction list */

  GList *packing_actions;	/* A GladeWidgetAction list, this actions are
				 * related to the container and they are not always present.
				 */

  GladeWidget    *lock; /* The glade widget that has locked this widget down.
			 */
  GList          *locked_widgets; /* A list of widgets this widget has locked down.
				   */

149
  GtkTreeModel   *signal_model; /* Signal model (or NULL if not yet requested) */
150
    
151
152
153
154
155
156
157
158
159
160
161
162
163
  /* Construct parameters: */
  GladeWidget       *construct_template;
  GladeCreateReason  construct_reason;
  gchar             *construct_internal;
  guint              construct_exact : 1;

  guint              in_project : 1;

  guint              visible : 1; /* Local copy of widget visibility, we need to keep track of this
				   * since the objects copy may be invalid due to a rebuild.
				   */
};

164
enum
Jose Maria Celorio's avatar
Jose Maria Celorio committed
165
{
166
167
168
169
170
171
172
173
  ADD_SIGNAL_HANDLER,
  REMOVE_SIGNAL_HANDLER,
  CHANGE_SIGNAL_HANDLER,
  BUTTON_PRESS_EVENT,
  BUTTON_RELEASE_EVENT,
  MOTION_NOTIFY_EVENT,
  SUPPORT_CHANGED,
  LAST_SIGNAL
174
};
Jose Maria Celorio's avatar
Jose Maria Celorio committed
175

176
enum
177
{
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  PROP_0,
  PROP_NAME,
  PROP_INTERNAL,
  PROP_ANARCHIST,
  PROP_OBJECT,
  PROP_ADAPTOR,
  PROP_PROJECT,
  PROP_PROPERTIES,
  PROP_PARENT,
  PROP_INTERNAL_NAME,
  PROP_TEMPLATE,
  PROP_TEMPLATE_EXACT,
  PROP_REASON,
  PROP_TOPLEVEL_WIDTH,
  PROP_TOPLEVEL_HEIGHT,
  PROP_SUPPORT_WARNING
194
195
};

196
static guint glade_widget_signals[LAST_SIGNAL] = { 0 };
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
197

198
static GQuark glade_widget_name_quark = 0;
199
200


201
202
203
204
205
#define IS_GLADE_WIDGET_EVENT(event)		 \
	((event) == GDK_BUTTON_PRESS ||		 \
	 (event) == GDK_BUTTON_RELEASE ||	 \
	 (event) == GDK_MOTION_NOTIFY)

206
G_DEFINE_TYPE (GladeWidget, glade_widget, G_TYPE_INITIALLY_UNOWNED)
207
208
209
/*******************************************************************************
                           GladeWidget class methods
 *******************************************************************************/
210
211
212
static void
glade_widget_set_packing_actions (GladeWidget * widget,
				  GladeWidget * parent)
213
{
214
  if (widget->priv->packing_actions)
215
    {
216
217
218
      g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->packing_actions);
      widget->priv->packing_actions = NULL;
219
220
    }

221
222
  widget->priv->packing_actions =
    glade_widget_adaptor_pack_actions_new (parent->priv->adaptor);
223
224
}

225
static void
226
227
glade_widget_add_child_impl (GladeWidget * widget,
                             GladeWidget * child, gboolean at_mouse)
228
{
229
  g_object_ref (child);
230

231
232
233
234
235
  /* Safe to set the parent first... setting it afterwards
   * creates packing properties, and that is not always
   * desirable.
   */
  glade_widget_set_parent (child, widget);
236

237
238
239
  /* Set packing actions first so we have access from the plugin
   */
  glade_widget_set_packing_actions (child, widget);
240

241
242
243
  glade_widget_adaptor_add (widget->priv->adaptor, 
			    widget->priv->object, 
			    child->priv->object);
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

  /* 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.
   */
  glade_widget_set_packing_properties (child, widget);
260
}
261

262
static void
263
glade_widget_remove_child_impl (GladeWidget * widget, GladeWidget * child)
264
{
265
  glade_widget_adaptor_remove (widget->priv->adaptor, widget->priv->object, child->priv->object);
266

267
  child->priv->parent = NULL;
268

269
  g_object_unref (child);
270
}
271

Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
272
static void
273
274
glade_widget_replace_child_impl (GladeWidget * widget,
                                 GObject * old_object, GObject * new_object)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
275
{
276
277
  GladeWidget *gnew_widget = glade_widget_get_from_gobject (new_object);
  GladeWidget *gold_widget = glade_widget_get_from_gobject (old_object);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
278

279
280
281
  if (gnew_widget)
    {
      g_object_ref (gnew_widget);
282

283
      gnew_widget->priv->parent = widget;
284

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

290
291
292
  if (gold_widget)
    {
      g_object_unref (gold_widget);
293

294
      if (gold_widget != gnew_widget)
295
        gold_widget->priv->parent = NULL;
296
    }
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
297

298
  glade_widget_adaptor_replace_child
299
      (widget->priv->adaptor, widget->priv->object, old_object, new_object);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
300

301
302
303
304
305
  /* Setup packing properties here so we can introspect the new
   * values from the backend.
   */
  if (gnew_widget)
    glade_widget_set_packing_properties (gnew_widget, widget);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
306
307
}

308
309
310
311
312
313
314
315
316
/**
 * glade_widget_add_signal_handler:
 * @widget: A #GladeWidget
 * @signal_handler: The #GladeSignal
 *
 * Adds a signal handler for @widget 
 */
void
glade_widget_add_signal_handler (GladeWidget *widget, const GladeSignal *signal_handler)
317
{
318
319
  GPtrArray *signals;
  GladeSignal *new_signal_handler;
320

321
322
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
323

324
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
325
326
327
  if (!signals)
    {
      signals = g_ptr_array_new ();
328
329
      g_hash_table_insert (widget->priv->signals, 
			   g_strdup (glade_signal_get_name (signal_handler)),
330
331
                           signals);
    }
332

333
334
  new_signal_handler = glade_signal_clone (signal_handler);
  g_ptr_array_add (signals, new_signal_handler);
335
  g_signal_emit (widget, glade_widget_signals[ADD_SIGNAL_HANDLER], 0, new_signal_handler);
336

337
  glade_project_verify_signal (widget, new_signal_handler);
338
339
}

340
341
342
343
344
345
346
347
/**
 * glade_widget_remove_signal_handler:
 * @widget: A #GladeWidget
 * @signal_handler: The #GladeSignal
 *
 * Removes a signal handler from @widget 
 */

Johannes Schmid's avatar
Johannes Schmid committed
348
void
349
glade_widget_remove_signal_handler (GladeWidget * widget,
Johannes Schmid's avatar
Johannes Schmid committed
350
                                    const GladeSignal * signal_handler)
351
{
352
353
354
  GPtrArray *signals;
  GladeSignal *tmp_signal_handler;
  guint i;
355

356
357
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
358

359
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
360

361
362
  /* trying to remove an inexistent signal? */
  g_assert (signals);
363

364
365
366
367
368
  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))
        {
369
		  g_signal_emit (widget, glade_widget_signals[REMOVE_SIGNAL_HANDLER], 0, tmp_signal_handler);
370
          g_object_unref (tmp_signal_handler);
371
372
373
374
          g_ptr_array_remove_index (signals, i);
          break;
        }
    }
375
}
376

377
378
379
380
381
382
383
384
385
/**
 * glade_widget_change_signal_handler:
 * @widget: A #GladeWidget
 * @old_signal_handler: the old #GladeSignal
 * @new_signal_handler: the new #GladeSignal
 *
 * Changes a #GladeSignal on @widget 
 */
void
386
387
388
glade_widget_change_signal_handler (GladeWidget * widget,
                                    const GladeSignal * old_signal_handler,
                                    const GladeSignal * new_signal_handler)
389
{
390
391
392
  GPtrArray *signals;
  GladeSignal *signal_handler_iter;
  guint i;
393

394
395
396
  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));
397
398
  g_return_if_fail (strcmp (glade_signal_get_name (old_signal_handler), 
			    glade_signal_get_name (new_signal_handler)) == 0);
399

400
  signals =
401
    glade_widget_list_signal_handlers (widget, glade_signal_get_name (old_signal_handler));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
402

403
404
  /* trying to remove an inexistent signal? */
  g_assert (signals);
405

406
407
408
409
410
411
  for (i = 0; i < signals->len; i++)
    {
      signal_handler_iter = g_ptr_array_index (signals, i);
      if (glade_signal_equal (signal_handler_iter, old_signal_handler))
        {
          /* Handler */
412
413
	  glade_signal_set_handler (signal_handler_iter, 
				    glade_signal_get_handler (new_signal_handler));
414
415

          /* Object */
416
417
418
419
420
421
422
423
	  glade_signal_set_userdata (signal_handler_iter, 
				     glade_signal_get_userdata (new_signal_handler));

	  /* Flags */
	  glade_signal_set_after (signal_handler_iter, 
				  glade_signal_get_after (new_signal_handler));
	  glade_signal_set_swapped (signal_handler_iter, 
				    glade_signal_get_swapped (new_signal_handler));
424
425
426
427
428

	  g_signal_emit (widget, glade_widget_signals[CHANGE_SIGNAL_HANDLER], 0, 
	                 signal_handler_iter);

	  break;
429
430
        }
    }
431
}
Tristan Van Berkom's avatar
Tristan Van Berkom committed
432

433
static gboolean
434
435
glade_widget_button_press_event_impl (GladeWidget * gwidget,
                                      GdkEvent * base_event)
436
{
437
438
439
  GtkWidget *widget;
  GdkEventButton *event = (GdkEventButton *) base_event;
  gboolean handled = FALSE;
440

441
442
443
444
  /* make sure to grab focus, since we may stop default handlers */
  widget = GTK_WIDGET (glade_widget_get_object (gwidget));
  if (gtk_widget_get_can_focus (widget) && !gtk_widget_has_focus (widget))
    gtk_widget_grab_focus (widget);
445

446
447
448
449
450
  /* if it's already selected don't stop default handlers, e.g. toggle button */
  if (event->button == 1)
    {
      if (event->state & GDK_CONTROL_MASK)
        {
451
          if (glade_project_is_selected (gwidget->priv->project, gwidget->priv->object))
452
            glade_project_selection_remove (gwidget->priv->project, gwidget->priv->object, TRUE);
453
          else
454
            glade_project_selection_add (gwidget->priv->project, gwidget->priv->object, TRUE);
455
456
          handled = TRUE;
        }
457
458
      else if (glade_project_is_selected (gwidget->priv->project,
                                          gwidget->priv->object) == FALSE)
459
        {
460
461
          glade_project_selection_set (gwidget->priv->project, 
				       gwidget->priv->object, TRUE);
462
463
464
465
466
467
468
469

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

471
472
473
474
475
476
  /* Give some kind of access in case of missing right button */
  if (!handled && glade_popup_is_popup_event (event))
    {
      glade_popup_widget_pop (gwidget, event, TRUE);
      handled = TRUE;
    }
477

478
  return handled;
479
}
480

481
static gboolean
482
glade_widget_event_impl (GladeWidget * gwidget, GdkEvent * event)
483
{
484
  gboolean handled = FALSE;
485

486
  g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
487

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  switch (event->type)
    {
      case GDK_BUTTON_PRESS:
        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;
      default:
        break;
    }
508

509
  return handled;
510
511
}

512
513
514
515
516
517
518
519

/**
 * glade_widget_event:
 * @event: A #GdkEvent
 *
 * Feed an event to be handled on the project GladeWidget
 * hierarchy.
 *
520
 * Returns: whether the event was handled or not.
521
522
 */
gboolean
523
glade_widget_event (GladeWidget * gwidget, GdkEvent * event)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
524
{
525
  gboolean handled = FALSE;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
526

527
528
529
  /* Lets just avoid some synthetic events (like focus-change) */
  if (((GdkEventAny *) event)->window == NULL)
    return FALSE;
530

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

533
#if 0
534
535
  if (event->type != GDK_EXPOSE)
    g_print ("event widget '%s' handled '%d' event '%d'\n",
536
             gwidget->priv->name, handled, event->type);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
537
#endif
538

539
  return handled;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
540
541
}

542
543
544
/*******************************************************************************
                      GObjectClass & Object Construction
 *******************************************************************************/
545
546
547

/*
 * This function creates new GObject parameters based on the GType of the 
548
 * GladeWidgetAdaptor and its default values.
549
550
551
552
553
 *
 * If a GladeWidget is specified, it will be used to apply the
 * values currently in use.
 */
static GParameter *
554
555
556
glade_widget_template_params (GladeWidget * widget,
                              gboolean construct, guint * n_params)
{
557
558
559
560
561
  GladeWidgetAdaptor *adaptor;
  GArray             *params;
  GObjectClass       *oclass;
  GParamSpec        **pspec;
  GladeProperty      *glade_property;
562
  GladePropertyClass *pclass;
563
  guint               n_props, i;
564

565
566
  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
  g_return_val_if_fail (n_params != NULL, NULL);
567

568
  adaptor = widget->priv->adaptor;
569

570
571
  /* As a slight optimization, we never unref the class
   */
572
  oclass = g_type_class_ref (glade_widget_adaptor_get_object_type (adaptor));
573
574
  pspec = g_object_class_list_properties (oclass, &n_props);
  params = g_array_new (FALSE, FALSE, sizeof (GParameter));
575

576
577
578
579
580
581
582
583
  for (i = 0; i < n_props; i++)
    {
      GParameter parameter = { 0, };

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

584
      pclass = glade_property_get_class (glade_property);
585
586
587

      /* Ignore properties based on some criteria
       */
588
589
      if (!glade_property_get_enabled (glade_property) || 
	  pclass == NULL ||     /* Unaccounted for in the builder */
590
591
592
          glade_property_class_get_virtual (pclass) ||  /* should not be set before 
							   GladeWidget wrapper exists */
          glade_property_class_get_ignore (pclass))     /* Catalog explicitly ignores the object */
593
594
595
596
597
598
599
600
601
602
        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;

603
      if (g_value_type_compatible (G_VALUE_TYPE (glade_property_class_get_default (pclass)),
604
605
606
                                   pspec[i]->value_type) == FALSE)
        {
          g_critical ("Type mismatch on %s property of %s",
607
608
                      parameter.name, 
		      glade_widget_adaptor_get_name (adaptor));
609
610
611
612
          continue;
        }

      if (g_param_values_cmp (pspec[i],
613
                              glade_property_inline_value (glade_property), 
614
			      glade_property_class_get_original_default (pclass)) == 0)
615
616
        continue;

617
618
619
      /* Not sure if it's safe to use glade_property_get_value() instead as the 
       * value type might differ than the real pspec 
       */
620
621
      parameter.name = pspec[i]->name;  /* These are not copied/freed */
      g_value_init (&parameter.value, pspec[i]->value_type);
622
      g_value_copy (glade_property_inline_value (glade_property), &parameter.value);
623
624
625
626

      g_array_append_val (params, parameter);
    }
  g_free (pspec);
627

628
629
  *n_params = params->len;
  return (GParameter *) g_array_free (params, FALSE);
630
631
632
}

static void
633
free_params (GParameter * params, guint n_params)
634
{
635
636
637
638
  gint i;
  for (i = 0; i < n_params; i++)
    g_value_unset (&(params[i].value));
  g_free (params);
639
640
641
642

}

static GObject *
643
644
glade_widget_build_object (GladeWidget * widget,
                           GladeWidget * template, GladeCreateReason reason)
645
{
646
647
648
  GParameter *params;
  GObject *object;
  guint n_params, i;
649

650
651
  if (reason == GLADE_CREATE_LOAD)
    {
652
      object = glade_widget_adaptor_construct_object (widget->priv->adaptor, 0, NULL);
653
654
655
      glade_widget_set_object (widget, object, TRUE);
      return object;
    }
656

657
658
659
660
  if (template)
    params = glade_widget_template_params (widget, TRUE, &n_params);
  else
    params =
661
        glade_widget_adaptor_default_params (widget->priv->adaptor, TRUE, &n_params);
662
663
664
665

  /* Create the new object with the correct parameters.
   */
  object =
666
      glade_widget_adaptor_construct_object (widget->priv->adaptor, n_params, params);
667

668
  free_params (params, n_params);
669

670
671
  /* Dont destroy toplevels when rebuilding, handle that separately */
  glade_widget_set_object (widget, object, reason != GLADE_CREATE_REBUILD);
672

673
674
675
676
  if (template)
    params = glade_widget_template_params (widget, FALSE, &n_params);
  else
    params =
677
        glade_widget_adaptor_default_params (widget->priv->adaptor, FALSE, &n_params);
678

679
  for (i = 0; i < n_params; i++)
680
    glade_widget_adaptor_set_property (widget->priv->adaptor, object, params[i].name,
681
                                       &(params[i].value));
682

683
  free_params (params, n_params);
684

685
  return object;
686
687
}

688
689
/**
 * glade_widget_dup_properties:
690
 * @dest_widget: the widget we are copying properties for
691
692
 * @template_props: the #GladeProperty list to copy
 * @as_load: whether to behave as if loading the project
693
694
 * @copy_parentless: whether to copy reffed widgets at all
 * @exact: whether to copy reffed widgets exactly
695
696
697
698
699
700
 *
 * 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.
 */
701
GList *
702
703
704
glade_widget_dup_properties (GladeWidget * dest_widget, GList * template_props,
                             gboolean as_load, gboolean copy_parentless,
                             gboolean exact)
705
{
706
  GList *list, *properties = NULL;
707

708
709
  for (list = template_props; list && list->data; list = list->next)
    {
710
711
      GladeProperty      *prop = list->data;
      GladePropertyClass *pclass = glade_property_get_class (prop);
712

713
      if (glade_property_class_save (pclass) == FALSE && as_load)
714
        continue;
715

716
      if (glade_property_class_parentless_widget (pclass) && copy_parentless)
717
718
719
        {
          GObject *object = NULL;
          GladeWidget *parentless;
720

721
          glade_property_get (prop, &object);
722

723
          prop = glade_property_dup (prop, NULL);
724

725
726
727
          if (object)
            {
              parentless = glade_widget_get_from_gobject (object);
728

729
              parentless = glade_widget_dup (parentless, exact);
730

731
              glade_widget_set_project (parentless, dest_widget->priv->project);
732

733
              glade_property_set (prop, parentless->priv->object);
734
735
736
737
            }
        }
      else
        prop = glade_property_dup (prop, NULL);
738
739


740
741
742
      properties = g_list_prepend (properties, prop);
    }
  return g_list_reverse (properties);
743
744
}

745
746
747
748
749
750
751
752
753
754
755
756
757
/**
 * 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
758
glade_widget_remove_property (GladeWidget * widget, const gchar * id_property)
759
{
760
  GladeProperty *prop;
761

762
763
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (id_property);
764

765
766
767
768
769
770
  /* XXX FIXME: currently we arent calling this on packing properties,
   * but doing so could cause crashes because the hash table is not
   * managed properly
   */
  if ((prop = glade_widget_get_property (widget, id_property)) != NULL)
    {
771
      widget->priv->properties = g_list_remove (widget->priv->properties, prop);
772
      g_hash_table_remove (widget->priv->props_hash, id_property);
773
774
775
776
      g_object_unref (prop);
    }
  else
    g_critical ("Couldnt find property %s on widget %s\n",
777
                id_property, widget->priv->name);
778
779
}

780
static void
781
glade_widget_set_catalog_defaults (GList * list)
782
{
783
784
785
  GList *l;
  for (l = list; l && l->data; l = l->next)
    {
786
      GladeProperty      *prop = l->data;
787
      GladePropertyClass *klass = glade_property_get_class (prop);
788
      GParamSpec         *pspec = glade_property_class_get_pspec (klass);
789

790
791
792
793
      if (glade_property_equals_value (prop, glade_property_class_get_original_default (klass)) &&
          g_param_values_cmp (pspec, 
			      glade_property_class_get_original_default (klass), 
			      glade_property_class_get_default (klass)))
794
795
        glade_property_reset (prop);
    }
796
}
797

798
static void
799
glade_widget_sync_custom_props (GladeWidget * widget)
800
{
801
  GList *l;
802
  for (l = widget->priv->properties; l && l->data; l = l->next)
803
    {
804
805
      GladeProperty      *prop = GLADE_PROPERTY (l->data);
      GladePropertyClass *pclass = glade_property_get_class (prop);
806

807
808
      if (glade_property_class_get_virtual (pclass) || 
	  glade_property_class_needs_sync (pclass))
809
810
        glade_property_sync (prop);
    }
811
812
813
}

static void
814
glade_widget_sync_packing_props (GladeWidget * widget)
815
{
816
  GList *l;
817
  for (l = widget->priv->packing_properties; l && l->data; l = l->next)
818
819
820
821
    {
      GladeProperty *prop = GLADE_PROPERTY (l->data);
      glade_property_sync (prop);
    }
822
823
}

824

825
static GObject *
826
827
828
glade_widget_constructor (GType type,
                          guint n_construct_properties,
                          GObjectConstructParam * construct_properties)
829
{
830
831
832
  GladeWidget *gwidget;
  GObject *ret_obj, *object;
  GList *properties = NULL, *list;
833

834
835
  ret_obj = G_OBJECT_CLASS (glade_widget_parent_class)->constructor
      (type, n_construct_properties, construct_properties);
836

837
  gwidget = GLADE_WIDGET (ret_obj);
838

839
  if (gwidget->priv->name == NULL)
840
    {
841
      if (gwidget->priv->internal)
842
843
        {
          gchar *name_base = g_strdup_printf ("%s-%s",
844
845
                                              gwidget->priv->construct_internal,
                                              gwidget->priv->internal);
846

847
          if (gwidget->priv->project)
848
            {
849
850
              gwidget->priv->name =
                  glade_project_new_widget_name (gwidget->priv->project,
851
852
853
854
                                                 gwidget, name_base);
              g_free (name_base);
            }
          else
855
            gwidget->priv->name = name_base;
856
857

        }
858
859
      else if (gwidget->priv->project)
        gwidget->priv->name = glade_project_new_widget_name
860
861
            (gwidget->priv->project, gwidget, 
	     glade_widget_adaptor_get_generic_name (gwidget->priv->adaptor));
862
      else
863
864
        gwidget->priv->name = 
	  g_strdup (glade_widget_adaptor_get_generic_name (gwidget->priv->adaptor));
865
    }
866

867
  if (gwidget->priv->construct_template)
868
869
    {
      properties = glade_widget_dup_properties
870
871
          (gwidget, gwidget->priv->construct_template->priv->properties, FALSE, TRUE,
           gwidget->priv->construct_exact);
872

873
874
      glade_widget_set_properties (gwidget, properties);
    }
875

876
  if (gwidget->priv->object == NULL)
877
878
    {
      object = glade_widget_build_object (gwidget,
879
880
                                          gwidget->priv->construct_template,
                                          gwidget->priv->construct_reason);
881
    }
882

883
884
  /* Copy sync parentless widget props here after a dup
   */
885
  if (gwidget->priv->construct_reason == GLADE_CREATE_COPY)
886
    {
887
      for (list = gwidget->priv->properties; list; list = list->next)
888
        {
889
890
891
          GladeProperty      *property = list->data;
	  GladePropertyClass *pclass = glade_property_get_class (property);

892
          if (glade_property_class_parentless_widget (pclass))
893
894
895
            glade_property_sync (property);
        }
    }
896

897
  /* Setup width/height */
898
899
  gwidget->priv->width = GWA_DEFAULT_WIDTH (gwidget->priv->adaptor);
  gwidget->priv->height = GWA_DEFAULT_HEIGHT (gwidget->priv->adaptor);
900
901
902
903
904

  /* 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).
   */
905
906
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
    for (list = gwidget->priv->properties; list; list = list->next)
907
908
909
      glade_property_load (GLADE_PROPERTY (list->data));

  /* We only use catalog defaults when the widget was created by the user! */
910
911
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
    glade_widget_set_catalog_defaults (gwidget->priv->properties);
912
913
914
915

  /* Only call this once the GladeWidget is completely built
   * (but before calling custom handlers...)
   */
916
917
  glade_widget_adaptor_post_create (gwidget->priv->adaptor,
                                    gwidget->priv->object, gwidget->priv->construct_reason);
918
919
920

  /* Virtual properties need to be explicitly synchronized.
   */
921
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
922
923
    glade_widget_sync_custom_props (gwidget);

924
925
  if (!gwidget->priv->internal && gwidget->priv->parent && 
      gwidget->priv->packing_properties == NULL)
926
    glade_widget_set_packing_properties (gwidget, gwidget->priv->parent);
927

928
929
  if (GTK_IS_WIDGET (gwidget->priv->object) &&
      !gtk_widget_is_toplevel (GTK_WIDGET (gwidget->priv->object)))
930
    {
931
932
      gwidget->priv->visible = TRUE;
      gtk_widget_show_all (GTK_WIDGET (gwidget->priv->object));
933
    }
934
935
  else if (GTK_IS_WIDGET (gwidget->priv->object) == FALSE)
    gwidget->priv->visible = TRUE;
936

937
  return ret_obj;
938
939
}

940
static void
941
glade_widget_finalize (GObject * object)
942
{
943
  GladeWidget *widget = GLADE_WIDGET (object);
944

945
  g_return_if_fail (GLADE_IS_WIDGET (object));
946

947
#if 0
948
  /* A good way to check if refcounts are balancing at project close time */
949
  g_print ("Finalizing widget %s\n", widget->priv->name);
950
951
#endif

952
953
  g_free (widget->priv->name);
  g_free (widget->priv->internal);
954
  g_free (widget->priv->construct_internal);
955
956
  g_free (widget->priv->support_warning);
  g_hash_table_destroy (widget->priv->signals);
957

958
959
960
961
  if (widget->priv->props_hash)
    g_hash_table_destroy (widget->priv->props_hash);
  if (widget->priv->pack_props_hash)
    g_hash_table_destroy (widget->priv->pack_props_hash);
962

963
  G_OBJECT_CLASS (glade_widget_parent_class)->finalize (object);
964
965
966
}

static void
967
reset_object_property (GladeProperty * property, GladeProject * project)
968
{
969
970
971
  GladePropertyClass *pclass = glade_property_get_class (property);

  if (glade_property_class_is_object (pclass))
972
    glade_property_reset (property);
973
974
}

975
static void
976
glade_widget_dispose (GObject * object)