glade-widget.c 132 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
G_DEFINE_TYPE (GladeWidget, glade_widget, G_TYPE_INITIALLY_UNOWNED)
201
202
203
/*******************************************************************************
                           GladeWidget class methods
 *******************************************************************************/
204
205
206
static void
glade_widget_set_packing_actions (GladeWidget * widget,
				  GladeWidget * parent)
207
{
208
  if (widget->priv->packing_actions)
209
    {
210
211
212
      g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->packing_actions);
      widget->priv->packing_actions = NULL;
213
214
    }

215
216
  widget->priv->packing_actions =
    glade_widget_adaptor_pack_actions_new (parent->priv->adaptor);
217
218
}

219
static void
220
221
glade_widget_add_child_impl (GladeWidget * widget,
                             GladeWidget * child, gboolean at_mouse)
222
{
223
  g_object_ref (child);
224

225
226
227
228
229
  /* Safe to set the parent first... setting it afterwards
   * creates packing properties, and that is not always
   * desirable.
   */
  glade_widget_set_parent (child, widget);
230

231
232
233
  /* Set packing actions first so we have access from the plugin
   */
  glade_widget_set_packing_actions (child, widget);
234

235
236
237
  glade_widget_adaptor_add (widget->priv->adaptor, 
			    widget->priv->object, 
			    child->priv->object);
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

  /* 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);
254
}
255

256
static void
257
glade_widget_remove_child_impl (GladeWidget * widget, GladeWidget * child)
258
{
259
  glade_widget_adaptor_remove (widget->priv->adaptor, widget->priv->object, child->priv->object);
260

261
  child->priv->parent = NULL;
262

263
  g_object_unref (child);
264
}
265

Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
266
static void
267
268
glade_widget_replace_child_impl (GladeWidget * widget,
                                 GObject * old_object, GObject * new_object)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
269
{
270
271
  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
272

273
274
275
  if (gnew_widget)
    {
      g_object_ref (gnew_widget);
276

277
      gnew_widget->priv->parent = widget;
278

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

284
285
286
  if (gold_widget)
    {
      g_object_unref (gold_widget);
287

288
      if (gold_widget != gnew_widget)
289
        gold_widget->priv->parent = NULL;
290
    }
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
291

292
  glade_widget_adaptor_replace_child
293
      (widget->priv->adaptor, widget->priv->object, old_object, new_object);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
294

295
296
297
298
299
  /* 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
300
301
}

302
303
304
305
306
307
308
309
310
/**
 * 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)
311
{
312
313
  GPtrArray *signals;
  GladeSignal *new_signal_handler;
314

315
316
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
317

318
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
319
320
  if (!signals)
    {
321
      signals = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
322
323
      g_hash_table_insert (widget->priv->signals, 
			   g_strdup (glade_signal_get_name (signal_handler)),
324
325
                           signals);
    }
326

327
328
  new_signal_handler = glade_signal_clone (signal_handler);
  g_ptr_array_add (signals, new_signal_handler);
329
  g_signal_emit (widget, glade_widget_signals[ADD_SIGNAL_HANDLER], 0, new_signal_handler);
330

331
  glade_project_verify_signal (widget, new_signal_handler);
332
333
}

334
335
336
337
338
339
340
341
/**
 * 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
342
void
343
glade_widget_remove_signal_handler (GladeWidget * widget,
Johannes Schmid's avatar
Johannes Schmid committed
344
                                    const GladeSignal * signal_handler)
345
{
346
347
348
  GPtrArray *signals;
  GladeSignal *tmp_signal_handler;
  guint i;
349

350
351
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
352

353
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
354

355
356
  /* trying to remove an inexistent signal? */
  g_assert (signals);
357

358
359
360
361
362
  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))
        {
363
		  g_signal_emit (widget, glade_widget_signals[REMOVE_SIGNAL_HANDLER], 0, tmp_signal_handler);
364
          g_object_unref (tmp_signal_handler);
365
366
367
368
          g_ptr_array_remove_index (signals, i);
          break;
        }
    }
369
}
370

371
372
373
374
375
376
377
378
379
/**
 * 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
380
381
382
glade_widget_change_signal_handler (GladeWidget * widget,
                                    const GladeSignal * old_signal_handler,
                                    const GladeSignal * new_signal_handler)
383
{
384
385
386
  GPtrArray *signals;
  GladeSignal *signal_handler_iter;
  guint i;
387

388
389
390
  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));
391
392
  g_return_if_fail (strcmp (glade_signal_get_name (old_signal_handler), 
			    glade_signal_get_name (new_signal_handler)) == 0);
393

394
  signals =
395
    glade_widget_list_signal_handlers (widget, glade_signal_get_name (old_signal_handler));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
396

397
398
  /* trying to remove an inexistent signal? */
  g_assert (signals);
399

400
401
402
403
404
405
  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 */
406
407
	  glade_signal_set_handler (signal_handler_iter, 
				    glade_signal_get_handler (new_signal_handler));
408
409

          /* Object */
410
411
412
413
414
415
416
417
	  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));
418
419
420
421
422

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

	  break;
423
424
        }
    }
425
}
Tristan Van Berkom's avatar
Tristan Van Berkom committed
426

427
static gboolean
428
429
glade_widget_button_press_event_impl (GladeWidget * gwidget,
                                      GdkEvent * base_event)
430
{
431
432
433
  GtkWidget *widget;
  GdkEventButton *event = (GdkEventButton *) base_event;
  gboolean handled = FALSE;
434

435
436
437
438
  /* 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);
439

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

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

465
466
467
468
469
470
  /* 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;
    }
471

472
  return handled;
473
}
474

475
static gboolean
476
glade_widget_event_impl (GladeWidget * gwidget, GdkEvent * event)
477
{
478
  gboolean handled = FALSE;
479

480
  g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
481

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
  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;
    }
502

503
  return handled;
504
505
}

506
507
508
509
510
511
512
513

/**
 * glade_widget_event:
 * @event: A #GdkEvent
 *
 * Feed an event to be handled on the project GladeWidget
 * hierarchy.
 *
514
 * Returns: whether the event was handled or not.
515
516
 */
gboolean
517
glade_widget_event (GladeWidget * gwidget, GdkEvent * event)
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
518
{
519
  gboolean handled = FALSE;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
520

521
522
523
  /* Lets just avoid some synthetic events (like focus-change) */
  if (((GdkEventAny *) event)->window == NULL)
    return FALSE;
524

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

527
#if 0
528
529
  if (event->type != GDK_EXPOSE)
    g_print ("event widget '%s' handled '%d' event '%d'\n",
530
             gwidget->priv->name, handled, event->type);
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
531
#endif
532

533
  return handled;
Tristan Van Berkom's avatar
updated    
Tristan Van Berkom committed
534
535
}

536
537
538
/*******************************************************************************
                      GObjectClass & Object Construction
 *******************************************************************************/
539
540
541

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

559
560
  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
  g_return_val_if_fail (n_params != NULL, NULL);
561

562
  adaptor = widget->priv->adaptor;
563

564
565
  /* As a slight optimization, we never unref the class
   */
566
  oclass = g_type_class_ref (glade_widget_adaptor_get_object_type (adaptor));
567
568
  pspec = g_object_class_list_properties (oclass, &n_props);
  params = g_array_new (FALSE, FALSE, sizeof (GParameter));
569

570
571
572
573
574
575
576
577
  for (i = 0; i < n_props; i++)
    {
      GParameter parameter = { 0, };

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

578
      pclass = glade_property_get_class (glade_property);
579
580
581

      /* Ignore properties based on some criteria
       */
582
583
      if (!glade_property_get_enabled (glade_property) || 
	  pclass == NULL ||     /* Unaccounted for in the builder */
584
585
586
          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 */
587
588
589
590
591
592
593
594
595
596
        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;

597
      if (g_value_type_compatible (G_VALUE_TYPE (glade_property_class_get_default (pclass)),
598
599
600
                                   pspec[i]->value_type) == FALSE)
        {
          g_critical ("Type mismatch on %s property of %s",
601
602
                      parameter.name, 
		      glade_widget_adaptor_get_name (adaptor));
603
604
605
606
          continue;
        }

      if (g_param_values_cmp (pspec[i],
607
                              glade_property_inline_value (glade_property), 
608
			      glade_property_class_get_original_default (pclass)) == 0)
609
610
        continue;

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

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

622
623
  *n_params = params->len;
  return (GParameter *) g_array_free (params, FALSE);
624
625
626
}

static void
627
free_params (GParameter * params, guint n_params)
628
{
629
630
631
632
  gint i;
  for (i = 0; i < n_params; i++)
    g_value_unset (&(params[i].value));
  g_free (params);
633
634
635
636

}

static GObject *
637
638
glade_widget_build_object (GladeWidget * widget,
                           GladeWidget * template, GladeCreateReason reason)
639
{
640
641
642
  GParameter *params;
  GObject *object;
  guint n_params, i;
643

644
645
  if (reason == GLADE_CREATE_LOAD)
    {
646
      object = glade_widget_adaptor_construct_object (widget->priv->adaptor, 0, NULL);
647
648
649
      glade_widget_set_object (widget, object, TRUE);
      return object;
    }
650

651
652
653
654
  if (template)
    params = glade_widget_template_params (widget, TRUE, &n_params);
  else
    params =
655
        glade_widget_adaptor_default_params (widget->priv->adaptor, TRUE, &n_params);
656
657
658
659

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

662
  free_params (params, n_params);
663

664
665
  /* Dont destroy toplevels when rebuilding, handle that separately */
  glade_widget_set_object (widget, object, reason != GLADE_CREATE_REBUILD);
666

667
668
669
670
  if (template)
    params = glade_widget_template_params (widget, FALSE, &n_params);
  else
    params =
671
        glade_widget_adaptor_default_params (widget->priv->adaptor, FALSE, &n_params);
672

673
  for (i = 0; i < n_params; i++)
674
    glade_widget_adaptor_set_property (widget->priv->adaptor, object, params[i].name,
675
                                       &(params[i].value));
676

677
  free_params (params, n_params);
678

679
  return object;
680
681
}

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

702
703
  for (list = template_props; list && list->data; list = list->next)
    {
704
705
      GladeProperty      *prop = list->data;
      GladePropertyClass *pclass = glade_property_get_class (prop);
706

707
      if (glade_property_class_save (pclass) == FALSE && as_load)
708
        continue;
709

710
      if (glade_property_class_parentless_widget (pclass) && copy_parentless)
711
712
713
        {
          GObject *object = NULL;
          GladeWidget *parentless;
714

715
          glade_property_get (prop, &object);
716

717
          prop = glade_property_dup (prop, NULL);
718

719
720
721
          if (object)
            {
              parentless = glade_widget_get_from_gobject (object);
722

723
              parentless = glade_widget_dup (parentless, exact);
724

725
              glade_widget_set_project (parentless, dest_widget->priv->project);
726

727
              glade_property_set (prop, parentless->priv->object);
728
729
730
731
            }
        }
      else
        prop = glade_property_dup (prop, NULL);
732
733


734
735
736
      properties = g_list_prepend (properties, prop);
    }
  return g_list_reverse (properties);
737
738
}

739
740
741
742
743
744
745
746
747
748
749
750
751
/**
 * 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
752
glade_widget_remove_property (GladeWidget * widget, const gchar * id_property)
753
{
754
  GladeProperty *prop;
755

756
757
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (id_property);
758

759
760
761
762
763
764
  /* 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)
    {
765
      widget->priv->properties = g_list_remove (widget->priv->properties, prop);
766
      g_hash_table_remove (widget->priv->props_hash, id_property);
767
768
769
770
      g_object_unref (prop);
    }
  else
    g_critical ("Couldnt find property %s on widget %s\n",
771
                id_property, widget->priv->name);
772
773
}

774
static void
775
glade_widget_set_catalog_defaults (GList * list)
776
{
777
778
779
  GList *l;
  for (l = list; l && l->data; l = l->next)
    {
780
      GladeProperty      *prop = l->data;
781
      GladePropertyClass *klass = glade_property_get_class (prop);
782
      GParamSpec         *pspec = glade_property_class_get_pspec (klass);
783

784
785
786
787
      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)))
788
789
        glade_property_reset (prop);
    }
790
}
791

792
static void
793
glade_widget_sync_custom_props (GladeWidget * widget)
794
{
795
  GList *l;
796
  for (l = widget->priv->properties; l && l->data; l = l->next)
797
    {
798
799
      GladeProperty      *prop = GLADE_PROPERTY (l->data);
      GladePropertyClass *pclass = glade_property_get_class (prop);
800

801
802
      if (glade_property_class_get_virtual (pclass) || 
	  glade_property_class_needs_sync (pclass))
803
804
        glade_property_sync (prop);
    }
805
806
807
}

static void
808
glade_widget_sync_packing_props (GladeWidget * widget)
809
{
810
  GList *l;
811
  for (l = widget->priv->packing_properties; l && l->data; l = l->next)
812
813
814
815
    {
      GladeProperty *prop = GLADE_PROPERTY (l->data);
      glade_property_sync (prop);
    }
816
817
}

818

819
static GObject *
820
821
822
glade_widget_constructor (GType type,
                          guint n_construct_properties,
                          GObjectConstructParam * construct_properties)
823
{
824
825
826
  GladeWidget *gwidget;
  GObject *ret_obj, *object;
  GList *properties = NULL, *list;
827

828
829
  ret_obj = G_OBJECT_CLASS (glade_widget_parent_class)->constructor
      (type, n_construct_properties, construct_properties);
830

831
  gwidget = GLADE_WIDGET (ret_obj);
832

833
  if (gwidget->priv->name == NULL)
834
    {
835
      if (gwidget->priv->internal)
836
837
        {
          gchar *name_base = g_strdup_printf ("%s-%s",
838
839
                                              gwidget->priv->construct_internal,
                                              gwidget->priv->internal);
840

841
          if (gwidget->priv->project)
842
            {
843
844
              gwidget->priv->name =
                  glade_project_new_widget_name (gwidget->priv->project,
845
846
847
848
                                                 gwidget, name_base);
              g_free (name_base);
            }
          else
849
            gwidget->priv->name = name_base;
850
851

        }
852
853
      else if (gwidget->priv->project)
        gwidget->priv->name = glade_project_new_widget_name
854
855
            (gwidget->priv->project, gwidget, 
	     glade_widget_adaptor_get_generic_name (gwidget->priv->adaptor));
856
      else
857
858
        gwidget->priv->name = 
	  g_strdup (glade_widget_adaptor_get_generic_name (gwidget->priv->adaptor));
859
    }
860

861
  if (gwidget->priv->construct_template)
862
863
    {
      properties = glade_widget_dup_properties
864
865
          (gwidget, gwidget->priv->construct_template->priv->properties, FALSE, TRUE,
           gwidget->priv->construct_exact);
866

867
868
      glade_widget_set_properties (gwidget, properties);
    }
869

870
  if (gwidget->priv->object == NULL)
871
872
    {
      object = glade_widget_build_object (gwidget,
873
874
                                          gwidget->priv->construct_template,
                                          gwidget->priv->construct_reason);
875
    }
876

877
878
  /* Copy sync parentless widget props here after a dup
   */
879
  if (gwidget->priv->construct_reason == GLADE_CREATE_COPY)
880
    {
881
      for (list = gwidget->priv->properties; list; list = list->next)
882
        {
883
884
885
          GladeProperty      *property = list->data;
	  GladePropertyClass *pclass = glade_property_get_class (property);

886
          if (glade_property_class_parentless_widget (pclass))
887
888
889
            glade_property_sync (property);
        }
    }
890

891
  /* Setup width/height */
892
893
  gwidget->priv->width = GWA_DEFAULT_WIDTH (gwidget->priv->adaptor);
  gwidget->priv->height = GWA_DEFAULT_HEIGHT (gwidget->priv->adaptor);
894
895
896
897
898

  /* 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).
   */
899
900
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
    for (list = gwidget->priv->properties; list; list = list->next)
901
902
903
      glade_property_load (GLADE_PROPERTY (list->data));

  /* We only use catalog defaults when the widget was created by the user! */
904
905
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
    glade_widget_set_catalog_defaults (gwidget->priv->properties);
906
907
908
909

  /* Only call this once the GladeWidget is completely built
   * (but before calling custom handlers...)
   */
910
911
  glade_widget_adaptor_post_create (gwidget->priv->adaptor,
                                    gwidget->priv->object, gwidget->priv->construct_reason);
912
913
914

  /* Virtual properties need to be explicitly synchronized.
   */
915
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
916
917
    glade_widget_sync_custom_props (gwidget);

918
919
  if (!gwidget->priv->internal && gwidget->priv->parent && 
      gwidget->priv->packing_properties == NULL)
920
    glade_widget_set_packing_properties (gwidget, gwidget->priv->parent);
921

922
923
  if (GTK_IS_WIDGET (gwidget->priv->object) &&
      !gtk_widget_is_toplevel (GTK_WIDGET (gwidget->priv->object)))
924
    {
925
926
      gwidget->priv->visible = TRUE;
      gtk_widget_show_all (GTK_WIDGET (gwidget->priv->object));
927
    }
928
929
  else if (GTK_IS_WIDGET (gwidget->priv->object) == FALSE)
    gwidget->priv->visible = TRUE;
930

931
  return ret_obj;
932
933
}

934
static void
935
glade_widget_finalize (GObject * object)
936
{
937
  GladeWidget *widget = GLADE_WIDGET (object);
938

939
  g_return_if_fail (GLADE_IS_WIDGET (object));
940

941
#if 0
942
  /* A good way to check if refcounts are balancing at project close time */
943
  g_print ("Finalizing widget %s\n", widget->priv->name);
944
945
#endif

946
947
  g_free (widget->priv->name);
  g_free (widget->priv->internal);
948
  g_free (widget->priv->construct_internal);
949
950
  g_free (widget->priv->support_warning);
  g_hash_table_destroy (widget->priv->signals);
951

952
953
954
955
  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);
956

957
  G_OBJECT_CLASS (glade_widget_parent_class)->finalize (object);
958
959
960
}

static void
961
reset_object_property (GladeProperty * property, GladeProject * project)
962
{
963
964
965
  GladePropertyClass *pclass = glade_property_get_class (property);

  if (glade_property_class_is_object (pclass))
966
    glade_property_reset (property);
967
968
}

969
static void
970
glade_widget_dispose (GObject * object)
971
{