glade-widget.c 150 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
#include "glade-private.h"
48
#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"
59
#include "glade-object-stub.h"
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
60
#include "glade-dnd.h"
61

62 63 64 65 66 67
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);
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 149

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.
				   */

150
  GtkTreeModel   *signal_model; /* Signal model (or NULL if not yet requested) */
151

152 153 154 155 156 157 158 159 160 161 162
  /* 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.
				   */
163
  guint              rebuilding : 1;
164
  guint              composite : 1;
165 166
};

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

179
enum
180
{
181 182 183 184 185
  PROP_0,
  PROP_NAME,
  PROP_INTERNAL,
  PROP_ANARCHIST,
  PROP_ADAPTOR,
186
  PROP_OBJECT,
187 188 189 190 191 192 193 194 195
  PROP_PROJECT,
  PROP_PROPERTIES,
  PROP_PARENT,
  PROP_INTERNAL_NAME,
  PROP_TEMPLATE,
  PROP_TEMPLATE_EXACT,
  PROP_REASON,
  PROP_TOPLEVEL_WIDTH,
  PROP_TOPLEVEL_HEIGHT,
196
  PROP_SUPPORT_WARNING,
197
  PROP_VISIBLE,
198
  PROP_COMPOSITE,
199
  N_PROPERTIES
200 201
};

202
static GParamSpec *properties[N_PROPERTIES];
203
static guint glade_widget_signals[LAST_SIGNAL] = { 0 };
Tristan Van Berkom's avatar
Tristan Van Berkom committed
204

205
static GQuark glade_widget_name_quark = 0;
206

Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
207 208 209
static void glade_widget_drag_init (_GladeDragInterface *iface);

G_DEFINE_TYPE_WITH_CODE (GladeWidget, glade_widget, G_TYPE_INITIALLY_UNOWNED,
210
                         G_ADD_PRIVATE (GladeWidget)
Juan Pablo Ugarte's avatar
Juan Pablo Ugarte committed
211 212 213
                         G_IMPLEMENT_INTERFACE (GLADE_TYPE_DRAG, 
                                                glade_widget_drag_init))

214 215 216
/*******************************************************************************
                           GladeWidget class methods
 *******************************************************************************/
217
static void
218 219
glade_widget_set_packing_actions (GladeWidget *widget,
				  GladeWidget *parent)
220
{
221
  if (widget->priv->packing_actions)
222
    {
223 224 225
      g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->packing_actions);
      widget->priv->packing_actions = NULL;
226 227
    }

228 229
  widget->priv->packing_actions =
    glade_widget_adaptor_pack_actions_new (parent->priv->adaptor);
230 231
}

232
static void
233 234
glade_widget_add_child_impl (GladeWidget *widget,
                             GladeWidget *child, gboolean at_mouse)
235
{
236
  g_object_ref (child);
237

238 239 240 241 242
  /* Safe to set the parent first... setting it afterwards
   * creates packing properties, and that is not always
   * desirable.
   */
  glade_widget_set_parent (child, widget);
243

244 245 246
  /* Set packing actions first so we have access from the plugin
   */
  glade_widget_set_packing_actions (child, widget);
247

248 249 250
  glade_widget_adaptor_add (widget->priv->adaptor, 
			    widget->priv->object, 
			    child->priv->object);
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266

  /* 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);
267
}
268

269
static void
270
glade_widget_remove_child_impl (GladeWidget *widget, GladeWidget *child)
271
{
272
  glade_widget_adaptor_remove (widget->priv->adaptor, widget->priv->object, child->priv->object);
273

274
  child->priv->parent = NULL;
275

276
  g_object_unref (child);
277
}
278

Tristan Van Berkom's avatar
Tristan Van Berkom committed
279
static void
280 281 282
glade_widget_replace_child_impl (GladeWidget *widget,
                                 GObject     *old_object,
                                 GObject     *new_object)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
283
{
284 285
  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
Tristan Van Berkom committed
286

287 288 289
  if (gnew_widget)
    {
      g_object_ref (gnew_widget);
290

291
      gnew_widget->priv->parent = widget;
292

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

298 299 300
  if (gold_widget)
    {
      g_object_unref (gold_widget);
301

302
      if (gold_widget != gnew_widget)
303
        gold_widget->priv->parent = NULL;
304
    }
Tristan Van Berkom's avatar
Tristan Van Berkom committed
305

306
  glade_widget_adaptor_replace_child
307
      (widget->priv->adaptor, widget->priv->object, old_object, new_object);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
308

309 310 311 312 313
  /* 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
Tristan Van Berkom committed
314 315
}

316 317 318 319 320 321 322 323
/**
 * glade_widget_add_signal_handler:
 * @widget: A #GladeWidget
 * @signal_handler: The #GladeSignal
 *
 * Adds a signal handler for @widget 
 */
void
324 325
glade_widget_add_signal_handler (GladeWidget       *widget,
                                 const GladeSignal *signal_handler)
326
{
327 328
  GPtrArray *signals;
  GladeSignal *new_signal_handler;
329

330 331
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
332

333
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
334 335
  if (!signals)
    {
336
      signals = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
337 338
      g_hash_table_insert (widget->priv->signals, 
			   g_strdup (glade_signal_get_name (signal_handler)),
339 340
                           signals);
    }
341

342 343
  new_signal_handler = glade_signal_clone (signal_handler);
  g_ptr_array_add (signals, new_signal_handler);
344
  g_signal_emit (widget, glade_widget_signals[ADD_SIGNAL_HANDLER], 0, new_signal_handler);
345

346
  glade_project_verify_signal (widget, new_signal_handler);
347 348 349

  if (glade_signal_get_support_warning (new_signal_handler))
    glade_widget_verify (widget);
350 351
}

352 353 354 355 356 357 358 359
/**
 * 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
360
void
361 362
glade_widget_remove_signal_handler (GladeWidget       *widget,
                                    const GladeSignal *signal_handler)
363
{
364 365 366
  GPtrArray *signals;
  GladeSignal *tmp_signal_handler;
  guint i;
367

368 369
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (GLADE_IS_SIGNAL (signal_handler));
370

371
  signals = glade_widget_list_signal_handlers (widget, glade_signal_get_name (signal_handler));
372

373 374
  /* trying to remove an inexistent signal? */
  g_assert (signals);
375

376 377 378 379 380
  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))
        {
381
	  g_signal_emit (widget, glade_widget_signals[REMOVE_SIGNAL_HANDLER], 0, tmp_signal_handler);
382
          g_ptr_array_remove_index (signals, i);
383 384 385 386 387

	  if (glade_signal_get_support_warning (tmp_signal_handler))
	    glade_widget_verify (widget);

          g_object_unref (tmp_signal_handler);
388 389 390
          break;
        }
    }
391
}
392

393 394 395 396 397 398 399 400 401
/**
 * 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
402 403 404
glade_widget_change_signal_handler (GladeWidget       *widget,
                                    const GladeSignal *old_signal_handler,
                                    const GladeSignal *new_signal_handler)
405
{
406 407 408
  GPtrArray *signals;
  GladeSignal *signal_handler_iter;
  guint i;
409

410 411 412
  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));
413 414
  g_return_if_fail (strcmp (glade_signal_get_name (old_signal_handler), 
			    glade_signal_get_name (new_signal_handler)) == 0);
415

416
  signals =
417
    glade_widget_list_signal_handlers (widget, glade_signal_get_name (old_signal_handler));
418

419 420
  /* trying to remove an inexistent signal? */
  g_assert (signals);
421

422 423 424 425 426
  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))
        {
427 428 429 430
          /* Detail */
	  glade_signal_set_detail (signal_handler_iter, 
				   glade_signal_get_detail (new_signal_handler));
          
431
          /* Handler */
432 433
	  glade_signal_set_handler (signal_handler_iter, 
				    glade_signal_get_handler (new_signal_handler));
434 435

          /* Object */
436 437 438 439 440 441 442 443
	  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));
444 445 446 447 448

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

	  break;
449 450
        }
    }
451
}
452

453
static gboolean
454 455
glade_widget_button_press_event_impl (GladeWidget *gwidget,
                                      GdkEvent    *base_event)
456
{
457 458 459
  GtkWidget *widget;
  GdkEventButton *event = (GdkEventButton *) base_event;
  gboolean handled = FALSE;
460

461 462 463 464
  /* 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);
465

466 467 468 469 470
  /* if it's already selected don't stop default handlers, e.g. toggle button */
  if (event->button == 1)
    {
      if (event->state & GDK_CONTROL_MASK)
        {
471
          if (glade_project_is_selected (gwidget->priv->project, gwidget->priv->object))
472
            glade_project_selection_remove (gwidget->priv->project, gwidget->priv->object, TRUE);
473
          else
474
            glade_project_selection_add (gwidget->priv->project, gwidget->priv->object, TRUE);
475 476
          handled = TRUE;
        }
477 478
      else if (glade_project_is_selected (gwidget->priv->project,
                                          gwidget->priv->object) == FALSE)
479
        {
480 481
          glade_project_selection_set (gwidget->priv->project, 
				       gwidget->priv->object, TRUE);
482 483 484 485 486 487 488 489

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

491 492 493 494 495 496
  /* 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;
    }
497

498
  return handled;
499
}
500

501
static gboolean
502
glade_widget_event_impl (GladeWidget *gwidget, GdkEvent *event)
503
{
504
  gboolean handled = FALSE;
505

506
  g_return_val_if_fail (GLADE_IS_WIDGET (gwidget), FALSE);
507

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
  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;
    }
528

529
  return handled;
530 531
}

532 533 534 535 536 537 538 539

/**
 * glade_widget_event:
 * @event: A #GdkEvent
 *
 * Feed an event to be handled on the project GladeWidget
 * hierarchy.
 *
540
 * Returns: whether the event was handled or not.
541 542
 */
gboolean
543
glade_widget_event (GladeWidget *gwidget, GdkEvent *event)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
544
{
545
  gboolean handled = FALSE;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
546

547 548 549
  /* Lets just avoid some synthetic events (like focus-change) */
  if (((GdkEventAny *) event)->window == NULL)
    return FALSE;
550

551
  handled = GLADE_WIDGET_GET_CLASS (gwidget)->event (gwidget, event);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
552

553
#ifdef GLADE_ENABLE_DEBUG
554
  if (event->type != GDK_EXPOSE)
555 556 557
    GLADE_NOTE (WIDGET_EVENTS,
		g_print ("event widget '%s' handled '%d' event '%d'\n",
			 gwidget->priv->name, handled, event->type));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
558
#endif
559

560
  return handled;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
561 562
}

563 564 565
/*******************************************************************************
                      GObjectClass & Object Construction
 *******************************************************************************/
566 567 568

/*
 * This function creates new GObject parameters based on the GType of the 
569
 * GladeWidgetAdaptor and its default values.
570 571 572 573 574
 *
 * If a GladeWidget is specified, it will be used to apply the
 * values currently in use.
 */
static GParameter *
575 576 577
glade_widget_template_params (GladeWidget *widget,
                              gboolean     construct,
                              guint       *n_params)
578
{
579 580 581 582 583
  GladeWidgetAdaptor *adaptor;
  GArray             *params;
  GObjectClass       *oclass;
  GParamSpec        **pspec;
  GladeProperty      *glade_property;
584
  GladePropertyClass *pclass;
585
  guint               n_props, i;
586

587 588
  g_return_val_if_fail (GLADE_IS_WIDGET (widget), NULL);
  g_return_val_if_fail (n_params != NULL, NULL);
589

590
  adaptor = widget->priv->adaptor;
591

592 593
  /* As a slight optimization, we never unref the class
   */
594
  oclass = g_type_class_ref (glade_widget_adaptor_get_object_type (adaptor));
595 596
  pspec = g_object_class_list_properties (oclass, &n_props);
  params = g_array_new (FALSE, FALSE, sizeof (GParameter));
597

598 599 600 601 602 603 604 605
  for (i = 0; i < n_props; i++)
    {
      GParameter parameter = { 0, };

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

606
      pclass = glade_property_get_class (glade_property);
607 608 609

      /* Ignore properties based on some criteria
       */
610 611
      if (!glade_property_get_enabled (glade_property) || 
	  pclass == NULL ||     /* Unaccounted for in the builder */
612 613 614
          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 */
615 616 617 618 619 620 621 622 623 624
        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;

625
      if (g_value_type_compatible (G_VALUE_TYPE (glade_property_class_get_default (pclass)),
626 627 628
                                   pspec[i]->value_type) == FALSE)
        {
          g_critical ("Type mismatch on %s property of %s",
629 630
                      parameter.name, 
		      glade_widget_adaptor_get_name (adaptor));
631 632 633
          continue;
        }

634 635 636 637 638 639
      /* We only check equality on properties introduced by the same class because
       * others properties could change its default in a derivated class
       * so its is better to transfer every property and reset them.
       */
      if (pspec[i]->owner_type == glade_widget_adaptor_get_object_type (adaptor) &&
          g_param_values_cmp (pspec[i],
640
                              glade_property_inline_value (glade_property), 
641
                              glade_property_class_get_original_default (pclass)) == 0)
642 643
        continue;

644 645 646
      /* Not sure if it's safe to use glade_property_get_value() instead as the 
       * value type might differ than the real pspec 
       */
647 648
      parameter.name = pspec[i]->name;  /* These are not copied/freed */
      g_value_init (&parameter.value, pspec[i]->value_type);
649
      g_value_copy (glade_property_inline_value (glade_property), &parameter.value);
650 651 652 653

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

655 656
  *n_params = params->len;
  return (GParameter *) g_array_free (params, FALSE);
657 658 659
}

static void
660
free_params (GParameter *params, guint n_params)
661
{
662 663 664 665
  gint i;
  for (i = 0; i < n_params; i++)
    g_value_unset (&(params[i].value));
  g_free (params);
666 667 668 669

}

static GObject *
670 671 672
glade_widget_build_object (GladeWidget      *widget,
                           GladeWidget      *template,
                           GladeCreateReason reason)
673
{
674 675 676
  GParameter *params;
  GObject *object;
  guint n_params, i;
677

678 679
  if (reason == GLADE_CREATE_LOAD)
    {
680
      object = glade_widget_adaptor_construct_object (widget->priv->adaptor, 0, NULL);
681
      glade_widget_set_object (widget, object);
682 683
      return object;
    }
684

685 686 687 688
  if (template)
    params = glade_widget_template_params (widget, TRUE, &n_params);
  else
    params =
689
        glade_widget_adaptor_default_params (widget->priv->adaptor, TRUE, &n_params);
690 691 692 693

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

696
  free_params (params, n_params);
697

698
  glade_widget_set_object (widget, object);
699

700 701 702 703
  if (template)
    params = glade_widget_template_params (widget, FALSE, &n_params);
  else
    params =
704
        glade_widget_adaptor_default_params (widget->priv->adaptor, FALSE, &n_params);
705

706
  for (i = 0; i < n_params; i++)
707
    glade_widget_adaptor_set_property (widget->priv->adaptor, object, params[i].name,
708
                                       &(params[i].value));
709

710
  free_params (params, n_params);
711

712
  return object;
713 714
}

715 716
/**
 * glade_widget_dup_properties:
717
 * @dest_widget: the widget we are copying properties for
718 719
 * @template_props: the #GladeProperty list to copy
 * @as_load: whether to behave as if loading the project
720 721
 * @copy_parentless: whether to copy reffed widgets at all
 * @exact: whether to copy reffed widgets exactly
722 723 724 725 726 727
 *
 * 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.
 */
728
GList *
729 730 731 732 733
glade_widget_dup_properties (GladeWidget *dest_widget,
                             GList       *template_props,
                             gboolean     as_load,
                             gboolean     copy_parentless,
                             gboolean     exact)
734
{
735
  GList *list, *properties = NULL;
736

737 738
  for (list = template_props; list && list->data; list = list->next)
    {
739 740
      GladeProperty      *prop = list->data;
      GladePropertyClass *pclass = glade_property_get_class (prop);
741

742
      if (glade_property_class_save (pclass) == FALSE && as_load)
743
        continue;
744

745
      if (glade_property_class_parentless_widget (pclass) && copy_parentless)
746 747 748
        {
          GObject *object = NULL;
          GladeWidget *parentless;
749

750
          glade_property_get (prop, &object);
751

752
          prop = glade_property_dup (prop, NULL);
753

754 755 756
          if (object)
            {
              parentless = glade_widget_get_from_gobject (object);
757

758
              parentless = glade_widget_dup (parentless, exact);
759

760
              glade_widget_set_project (parentless, dest_widget->priv->project);
761

762
              glade_property_set (prop, parentless->priv->object);
763 764 765 766
            }
        }
      else
        prop = glade_property_dup (prop, NULL);
767 768


769 770 771
      properties = g_list_prepend (properties, prop);
    }
  return g_list_reverse (properties);
772 773
}

774 775 776 777 778 779 780 781 782 783 784 785 786
/**
 * 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
787
glade_widget_remove_property (GladeWidget *widget, const gchar *id_property)
788
{
789
  GladeProperty *prop;
790

791 792
  g_return_if_fail (GLADE_IS_WIDGET (widget));
  g_return_if_fail (id_property);
793

794 795 796 797 798 799
  /* 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)
    {
800
      widget->priv->properties = g_list_remove (widget->priv->properties, prop);
801
      g_hash_table_remove (widget->priv->props_hash, id_property);
802 803 804 805
      g_object_unref (prop);
    }
  else
    g_critical ("Couldnt find property %s on widget %s\n",
806
                id_property, widget->priv->name);
807 808
}

809
static void
810
glade_widget_set_catalog_defaults (GList *list)
811
{
812 813 814
  GList *l;
  for (l = list; l && l->data; l = l->next)
    {
815
      GladeProperty      *prop = l->data;
816
      GladePropertyClass *klass = glade_property_get_class (prop);
817
      GParamSpec         *pspec = glade_property_class_get_pspec (klass);
818

819 820 821 822
      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)))
823 824
        glade_property_reset (prop);
    }
825
}
826

827
static void
828
glade_widget_sync_custom_props (GladeWidget *widget)
829
{
830
  GList *l;
831
  for (l = widget->priv->properties; l && l->data; l = l->next)
832
    {
833 834
      GladeProperty      *prop = GLADE_PROPERTY (l->data);
      GladePropertyClass *pclass = glade_property_get_class (prop);
835

836 837
      if (glade_property_class_get_virtual (pclass) || 
	  glade_property_class_needs_sync (pclass))
838 839
        glade_property_sync (prop);
    }
840 841 842
}

static void
843
glade_widget_sync_packing_props (GladeWidget *widget)
844
{
845
  GList *l;
846
  for (l = widget->priv->packing_properties; l && l->data; l = l->next)
847 848 849 850
    {
      GladeProperty *prop = GLADE_PROPERTY (l->data);
      glade_property_sync (prop);
    }
851 852
}

853

854
static GObject *
855 856 857
glade_widget_constructor (GType                  type,
                          guint                  n_construct_properties,
                          GObjectConstructParam *construct_properties)
858
{
859
  GladeWidget *gwidget;
860
  GObject *ret_obj;
861
  GList *properties = NULL, *list;
862

863 864
  ret_obj = G_OBJECT_CLASS (glade_widget_parent_class)->constructor
      (type, n_construct_properties, construct_properties);
865

866
  gwidget = GLADE_WIDGET (ret_obj);
867

868
  if (gwidget->priv->name == NULL)
869
    {
870 871
      if (gwidget->priv->project)
        gwidget->priv->name = glade_project_new_widget_name (gwidget->priv->project, gwidget, GLADE_UNNAMED_PREFIX);
872
      else
873
        gwidget->priv->name = g_strdup (GLADE_UNNAMED_PREFIX);
874
    }
875

876
  if (gwidget->priv->construct_template)
877 878
    {
      properties = glade_widget_dup_properties
879 880
          (gwidget, gwidget->priv->construct_template->priv->properties, FALSE, TRUE,
           gwidget->priv->construct_exact);
881

882 883
      glade_widget_set_properties (gwidget, properties);
    }
884

885
  if (gwidget->priv->object == NULL)
886 887 888
    glade_widget_build_object (gwidget,
                               gwidget->priv->construct_template,
                               gwidget->priv->construct_reason);
889

890 891
  /* Copy sync parentless widget props here after a dup
   */
892
  if (gwidget->priv->construct_reason == GLADE_CREATE_COPY)
893
    {
894
      for (list = gwidget->priv->properties; list; list = list->next)
895
        {
896 897 898
          GladeProperty      *property = list->data;
	  GladePropertyClass *pclass = glade_property_get_class (property);

899
          if (glade_property_class_parentless_widget (pclass))
900 901 902
            glade_property_sync (property);
        }
    }
903

904
  /* Setup width/height */
905 906
  gwidget->priv->width = GWA_DEFAULT_WIDTH (gwidget->priv->adaptor);
  gwidget->priv->height = GWA_DEFAULT_HEIGHT (gwidget->priv->adaptor);
907 908 909 910 911

  /* 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).
   */
912 913
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
    for (list = gwidget->priv->properties; list; list = list->next)
914 915
      glade_property_load (GLADE_PROPERTY (list->data));

916 917 918 919 920
  /* We only use catalog defaults when the widget was created by the user!
   * and or is not an internal widget.
   */
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER &&
      gwidget->priv->internal == NULL)
921
    glade_widget_set_catalog_defaults (gwidget->priv->properties);
922 923 924 925

  /* Only call this once the GladeWidget is completely built
   * (but before calling custom handlers...)
   */
926 927
  glade_widget_adaptor_post_create (gwidget->priv->adaptor,
                                    gwidget->priv->object, gwidget->priv->construct_reason);
928 929 930

  /* Virtual properties need to be explicitly synchronized.
   */
931
  if (gwidget->priv->construct_reason == GLADE_CREATE_USER)
932 933
    glade_widget_sync_custom_props (gwidget);

934
  if (gwidget->priv->parent && gwidget->priv->packing_properties == NULL)
935
    glade_widget_set_packing_properties (gwidget, gwidget->priv->parent);
936

937 938
  if (GTK_IS_WIDGET (gwidget->priv->object) &&
      !gtk_widget_is_toplevel (GTK_WIDGET (gwidget->priv->object)))
939
    {
940 941
      gwidget->priv->visible = TRUE;
      gtk_widget_show_all (GTK_WIDGET (gwidget->priv->object));
942
    }
943 944
  else if (GTK_IS_WIDGET (gwidget->priv->object) == FALSE)
    gwidget->priv->visible = TRUE;
945

946 947 948
  /* Verify support warnings to start off */
  glade_widget_verify (gwidget);

949
  if (g_str_has_prefix (glade_widget_adaptor_get_name (gwidget->priv->adaptor),
950
                        GWA_INSTANTIABLE_PREFIX))
951 952
    glade_widget_set_is_composite (gwidget, TRUE);

953
  return ret_obj;
954 955
}

956
static void
957
glade_widget_finalize (GObject *object)
958
{
959
  GladeWidget *widget = GLADE_WIDGET (object);
960

961
  g_return_if_fail (GLADE_IS_WIDGET (object));
962

963 964
  GLADE_NOTE (REF_COUNTS,
	      g_print ("Finalizing widget %s\n", widget->priv->name));
965

966 967
  g_free (widget->priv->name);
  g_free (widget->priv->internal);
968
  g_free (widget->priv->construct_internal);
969 970
  g_free (widget->priv->support_warning);
  g_hash_table_destroy (widget->priv->signals);
971

972 973 974 975
  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);
976

977
  G_OBJECT_CLASS (glade_widget_parent_class)->finalize (object);
978 979 980
}

static void
981
reset_object_property (GladeProperty *property, GladeProject *project)
982
{
983 984 985
  GladePropertyClass *pclass = glade_property_get_class (property);

  if (glade_property_class_is_object (pclass))
986
    glade_property_reset (property);
987 988
}

989
static void
990
glade_widget_dispose (GObject *object)
991
{
992
  GladeWidget *widget = GLADE_WIDGET (object);
993
  GList *children, *l;
994

995
  glade_widget_push_superuser ();
996

997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
  /* Remove all children at dispose */
  children = glade_widget_get_children (widget);
  for (l = children; l; l = l->next)
    {
      GladeWidget *child = glade_widget_get_from_gobject (l->data);

      if (glade_widget_get_internal (child) == NULL)
	glade_widget_remove_child (widget, child);
    }
  g_list_free (children);

1008
  /* Release references by way of object properties... */
1009
  while (widget->priv->prop_refs)
1010
    {
1011
      GladeProperty *property = GLADE_PROPERTY (widget->priv->prop_refs->data);
1012 1013
      glade_property_set (property, NULL);
    }
1014

1015 1016 1017
  if (widget->priv->properties)
    g_list_foreach (widget->priv->properties, (GFunc) reset_object_property,
                    widget->priv->project);
1018

1019 1020
  /* We have to make sure properties release thier references on other widgets first 
   * hence the reset (for object properties) */
1021
  if (widget->priv->properties)
1022
    {
1023 1024 1025
      g_list_foreach (widget->priv->properties, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->properties);
      widget->priv->properties = NULL;
1026
    }
1027 1028 1029 1030 1031
  if (widget->priv->props_hash)
    {
      g_hash_table_destroy (widget->priv->props_hash);
      widget->priv->props_hash = NULL;
    }
1032

1033
  glade_widget_set_object (widget, NULL);
1034

1035
  if (widget->priv->packing_properties)
1036
    {
1037 1038 1039
      g_list_foreach (widget->priv->packing_properties, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->packing_properties);
      widget->priv->packing_properties = NULL;
1040 1041
    }

1042
  if (widget->priv->actions)
1043
    {
1044 1045 1046
      g_list_foreach (widget->priv->actions, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->actions);
      widget->priv->actions = NULL;
1047 1048
    }

1049
  if (widget->priv->packing_actions)
1050
    {
1051 1052 1053
      g_list_foreach (widget->priv->packing_actions, (GFunc) g_object_unref, NULL);
      g_list_free (widget->priv->packing_actions);
      widget->priv->packing_actions = NULL;
1054
    }
1055

1056
  if (widget->priv->signal_model)
1057
    {
1058 1059
      g_object_unref (widget->priv->signal_model);
      widget->priv->signal_model = NULL;
1060 1061
    }

1062 1063 1064
  glade_widget_pop_superuser ();

  G_OBJECT_CLASS (glade_widget_parent_class)->dispose (object);
1065 1066
}

1067
static void
1068 1069 1070 1071
glade_widget_set_real_property (GObject      *object,
                                guint         prop_id,
                                const GValue *value,
                                GParamSpec   *pspec)
1072
{
1073
  GladeWidget *widget;
1074

1075
  widget = GLADE_WIDGET (object);
1076

1077 1078 1079 1080 1081 1082 1083 1084 1085
  switch (prop_id)
    {
      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:
1086
        widget->priv->anarchist = g_value_get_boolean (value);
1087 1088 1089
        break;
      case PROP_OBJECT:
        if (g_value_get_object (value))
1090
          glade_widget_set_object (widget, g_value_get_object (value));
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
        break;
      case PROP_PROJECT:
        glade_widget_set_project (widget,
                                  GLADE_PROJECT (g_value_get_object (value)));
        break;
      case PROP_ADAPTOR:
        glade_widget_set_adaptor (widget, GLADE_WIDGET_ADAPTOR
                                  (g_value_get_object (value)));
        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;
      case PROP_INTERNAL_NAME:
        if (g_value_get_string (value))
1110
          widget->priv->construct_internal = g_value_dup_string (value);
1111 1112
        break;
      case PROP_TEMPLATE:
1113
        widget->priv->construct_template = g_value_get_object (value);
1114 1115
        break;
      case PROP_TEMPLATE_EXACT:
1116
        widget->priv->construct_exact = g_value_get_boolean (value);
1117 1118
        break;
      case PROP_REASON:
1119
        widget->priv->construct_reason = g_value_get_int (value);
1120 1121
        break;
      case PROP_TOPLEVEL_WIDTH:
1122
        widget->priv->width = g_value_get_int (value);
1123 1124
        break;
      case PROP_TOPLEVEL_HEIGHT:
1125
        widget->priv->height = g_value_get_int (value);
1126
        break;
1127 1128 1129
    case PROP_COMPOSITE:
        glade_widget_set_is_composite (widget, g_value_get_boolean (value));
	break;
1130 1131 1132 1133
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
1134 1135 1136
}

static void
1137 1138 1139 1140
glade_widget_get_real_property (GObject    *object,
                                guint       prop_id,
                                GValue     *value,
                                GParamSpec *pspec)
1141
{
1142
  GladeWidget *widget;
1143

1144
  widget = GLADE_WIDGET (object);
1145

1146 1147 1148
  switch (prop_id)
    {
      case PROP_NAME:
1149
        g_value_set_string (value, widget->priv->name);
1150 1151
        break;
      case PROP_INTERNAL:
1152
        g_value_set_string (value, widget->priv->internal);
1153 1154
        break;
      case PROP_ANARCHIST:
1155
        g_value_set_boolean (value, widget->priv->anarchist);
1156 1157
        break;
      case PROP_ADAPTOR:
1158
        g_value_set_object (value, widget->priv->adaptor);