glade-gtk.c 404 KB
Newer Older
1
2
/*
 * Copyright (C) 2001 Ximian, Inc.
3
 * Copyright (C) 2004 - 2008 Tristan Van Berkom, Juan Pablo Ugarte et al.
4
5
6
7
8
9
10
11
12
13
14
15
16
 *
 * 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
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19
20
 *
 * Authors:
 *   Chema Celorio <chema@celorio.com>
21
22
 *   Tristan Van Berkom <tvb@gnome.org>
 *   Juan Pablo Ugarte <juanpablougarte@gmail.com>
23
24
 */

25
#include <config.h>
26

27
#include "glade-gtk.h"
28
#include "glade-accels.h"
29
#include "glade-attributes.h"
30
#include "glade-column-types.h"
31
#include "glade-model-data.h"
32
#include "glade-icon-sources.h"
33
#include "glade-button-editor.h"
34
#include "glade-tool-button-editor.h"
35
#include "glade-image-editor.h"
36
#include "glade-image-item-editor.h"
37
#include "glade-icon-factory-editor.h"
38
#include "glade-store-editor.h"
39
#include "glade-label-editor.h"
40
#include "glade-cell-renderer-editor.h"
41
#include "glade-treeview-editor.h"
42
#include "glade-entry-editor.h"
43
#include "glade-activatable-editor.h"
44
#include "glade-tool-item-group-editor.h"
45
#include "glade-string-list.h"
46
#include "glade-fixed.h"
47

48
49
#include <gladeui/glade-editor-property.h>
#include <gladeui/glade-base-editor.h>
50
#include <gladeui/glade-xml-utils.h>
51

52

53
#include <gtk/gtk.h>
54
#include <glib/gi18n-lib.h>
55
56
#include <string.h>
#include <stdlib.h>
57

58
/* --------------------------------- Constants ------------------------------ */
59
60
61
#define FIXED_DEFAULT_CHILD_WIDTH  100
#define FIXED_DEFAULT_CHILD_HEIGHT 60

62
63
#define MNEMONIC_INSENSITIVE_MSG   _("This property does not apply unless Use Underline is set.")
#define NOT_SELECTED_MSG           _("Property not selected")
64
#define RESPID_INSENSITIVE_MSG     _("This property is only for use in dialog action buttons")
65
#define ACTION_APPEARANCE_MSG      _("This property is set to be controlled by an Action")
66
/* -------------------------------- ParamSpecs ------------------------------ */
67

68
/* Fake GtkImage::icon-size since its an int pspec in the image */
69
GParamSpec *
70
gladegtk_icon_size_spec (void)
71
{
72
73
74
75
76
  return g_param_spec_enum ("icon-size", _("Icon Size"),
                            _
                            ("Symbolic size to use for stock icon, icon set or named icon"),
                            GTK_TYPE_ICON_SIZE, GTK_ICON_SIZE_BUTTON,
                            G_PARAM_READWRITE);
77
78
}

79
80
81
/* This function does absolutely nothing
 * (and is for use in overriding post_create functions).
 */
82
void
83
empty (GObject * container, GladeCreateReason reason)
84
85
86
{
}

87
88
/* This function is used to stop default handlers  */
static void
89
90
glade_gtk_stop_emission_POINTER (gpointer instance, gpointer dummy,
                                 gpointer data)
91
{
92
  g_signal_stop_emission (instance, GPOINTER_TO_UINT (data), 0);
93
}
94

95
96
97
98


/* Initialize needed pspec types from here */
void
99
glade_gtk_init (const gchar * name)
100
101
102
103
{

}

104
/* ----------------------------- GtkWidget ------------------------------ */
105
gboolean
106
107
glade_gtk_widget_depends (GladeWidgetAdaptor * adaptor,
                          GladeWidget * widget, GladeWidget * another)
108
{
109
110
111
  if (GTK_IS_ICON_FACTORY (glade_widget_get_object (another)) ||
      GTK_IS_ACTION (glade_widget_get_object (another)) || 
      GTK_IS_ACTION_GROUP (glade_widget_get_object (another)))
112
    return TRUE;
113

114
  return GWA_GET_CLASS (G_TYPE_OBJECT)->depends (adaptor, widget, another);
115
116
}

117
#define GLADE_TAG_A11Y_A11Y         "accessibility"
118
#define GLADE_TAG_A11Y_ACTION_NAME  "action_name"       /* We should make -/_ synonymous */
119
120
121
122
#define GLADE_TAG_A11Y_DESC         "description"
#define GLADE_TAG_A11Y_TARGET       "target"
#define GLADE_TAG_A11Y_TYPE         "type"

123
124
#define GLADE_TAG_A11Y_INTERNAL_NAME         "accessible"

125
126
#define GLADE_TAG_ATTRIBUTES        "attributes"
#define GLADE_TAG_ATTRIBUTE         "attribute"
127
128
129
#define GLADE_TAG_A11Y_RELATION     "relation"
#define GLADE_TAG_A11Y_ACTION       "action"
#define GLADE_TAG_A11Y_PROPERTY     "property"
130
131


132
static const gchar *atk_relations_list[] = {
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
  "controlled-by",
  "controller-for",
  "labelled-by",
  "label-for",
  "member-of",
  "node-child-of",
  "flows-to",
  "flows-from",
  "subwindow-of",
  "embeds",
  "embedded-by",
  "popup-for",
  "parent-window-of",
  "described-by",
  "description-for",
  NULL
149
150
151
};

static void
152
153
glade_gtk_read_accels (GladeWidget * widget,
                       GladeXmlNode * node, gboolean require_signal)
154
{
155
156
157
158
159
  GladeProperty *property;
  GladeXmlNode *prop;
  GladeAccelInfo *ainfo;
  GValue *value = NULL;
  GList *accels = NULL;
160

161
162
163
164
165
  for (prop = glade_xml_node_get_children (node);
       prop; prop = glade_xml_node_next (prop))
    {
      if (!glade_xml_node_verify_silent (prop, GLADE_TAG_ACCEL))
        continue;
166

167
168
169
      if ((ainfo = glade_accel_read (prop, require_signal)) != NULL)
        accels = g_list_prepend (accels, ainfo);
    }
170

171
172
173
174
175
  if (accels)
    {
      value = g_new0 (GValue, 1);
      g_value_init (value, GLADE_TYPE_ACCEL_GLIST);
      g_value_take_boxed (value, accels);
176

177
178
      property = glade_widget_get_property (widget, "accelerator");
      glade_property_set_value (property, value);
179

180
181
182
      g_value_unset (value);
      g_free (value);
    }
183
184
185
}

static void
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
glade_gtk_parse_atk_props (GladeWidget * widget, GladeXmlNode * node)
{
  GladeXmlNode *prop;
  GladeProperty *property;
  GValue *gvalue;
  gchar *value, *name, *id, *comment;
  gint translatable;
  gboolean is_action;

  for (prop = glade_xml_node_get_children (node);
       prop; prop = glade_xml_node_next (prop))
    {
      if (glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_PROPERTY))
        is_action = FALSE;
      else if (glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_ACTION))
        is_action = TRUE;
      else
        continue;

      if (!is_action &&
          !(name = glade_xml_get_property_string_required
            (prop, GLADE_XML_TAG_NAME, NULL)))
        continue;
      else if (is_action &&
               !(name = glade_xml_get_property_string_required
                 (prop, GLADE_TAG_A11Y_ACTION_NAME, NULL)))
        continue;


      /* Make sure we are working with dashes and
       * not underscores ... 
       */
      id = glade_util_read_prop_name (name);
      g_free (name);

      /* We are namespacing the action properties internally
       * just incase they clash (all property names must be
       * unique...)
       */
      if (is_action)
        {
          name = g_strdup_printf ("atk-%s", id);
          g_free (id);
          id = name;
        }
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
      if ((property = glade_widget_get_property (widget, id)) != NULL)
        {
          /* Complex statement just getting the value here... */
          if ((!is_action &&
               !(value = glade_xml_get_content (prop))) ||
              (is_action &&
               !(value = glade_xml_get_property_string_required
                 (prop, GLADE_TAG_A11Y_DESC, NULL))))
            {
              /* XXX should be a glade_xml_get_content_required()... */
              g_free (id);
              continue;
            }

          /* Set the parsed value on the property ... */
          gvalue = glade_property_class_make_gvalue_from_string
248
	    (glade_property_get_class (property), value, glade_widget_get_project (widget));
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
          glade_property_set_value (property, gvalue);
          g_value_unset (gvalue);
          g_free (gvalue);

          /* Deal with i18n... ... XXX Do i18n context !!! */
          translatable = glade_xml_get_property_boolean
              (prop, GLADE_TAG_TRANSLATABLE, FALSE);
          comment = glade_xml_get_property_string (prop, GLADE_TAG_COMMENT);

          glade_property_i18n_set_translatable (property, translatable);
          glade_property_i18n_set_comment (property, comment);

          g_free (comment);
          g_free (value);
        }

      g_free (id);
    }
267
268
}

269
static void
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
glade_gtk_parse_atk_props_gtkbuilder (GladeWidget * widget, GladeXmlNode * node)
{
  GladeXmlNode *child, *object_node;
  gchar *internal;

  /* Search for internal "accessible" child and redirect parse from there */
  for (child = glade_xml_node_get_children (node);
       child; child = glade_xml_node_next (child))
    {
      if (glade_xml_node_verify_silent (child, GLADE_XML_TAG_CHILD))
        {
          if ((internal =
               glade_xml_get_property_string (child,
                                              GLADE_XML_TAG_INTERNAL_CHILD)))
            {
              if (!strcmp (internal, GLADE_TAG_A11Y_INTERNAL_NAME) &&
                  (object_node =
                   glade_xml_search_child_required (child,
                                                    GLADE_XML_TAG_WIDGET)))
                glade_gtk_parse_atk_props (widget, object_node);

              g_free (internal);
            }
        }
    }
295
296
297
}

static void
298
299
300
glade_gtk_parse_atk_relation (GladeProperty * property, GladeXmlNode * node)
{
  GladeXmlNode *prop;
301
  GladePropertyClass *pclass;
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
  gchar *type, *target, *id, *tmp;
  gchar *string = NULL;

  for (prop = glade_xml_node_get_children (node);
       prop; prop = glade_xml_node_next (prop))
    {
      if (!glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_RELATION))
        continue;

      if (!(type =
            glade_xml_get_property_string_required
            (prop, GLADE_TAG_A11Y_TYPE, NULL)))
        continue;

      if (!(target =
            glade_xml_get_property_string_required
            (prop, GLADE_TAG_A11Y_TARGET, NULL)))
        {
          g_free (type);
          continue;
        }
323

324
325
      id     = glade_util_read_prop_name (type);
      pclass = glade_property_get_class (property);
326

327
      if (!strcmp (id, glade_property_class_id (pclass)))
328
329
330
331
332
333
334
335
336
        {
          if (string == NULL)
            string = g_strdup (target);
          else
            {
              tmp = g_strdup_printf ("%s%s%s", string,
                                     GPC_OBJECT_DELIMITER, target);
              string = (g_free (string), tmp);
            }
337

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
        }

      g_free (id);
      g_free (type);
      g_free (target);
    }


  /* we must synchronize this directly after loading this project
   * (i.e. lookup the actual objects after they've been parsed and
   * are present). this is a feature of object and object list properties
   * that needs a better api.
   */
  if (string)
    {
      g_object_set_data_full (G_OBJECT (property), "glade-loaded-object",
354
355
			      /* 'string' here is already allocated on the heap */
                              string, g_free);
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
    }
}

static void
glade_gtk_widget_read_atk_props (GladeWidget * widget, GladeXmlNode * node)
{
  GladeXmlNode *atk_node;
  GladeProperty *property;
  gint i;

  glade_gtk_parse_atk_props_gtkbuilder (widget, node);

  if ((atk_node = glade_xml_search_child (node, GLADE_TAG_A11Y_A11Y)) != NULL)
    {
      /* Properties & actions */
      glade_gtk_parse_atk_props (widget, atk_node);

      /* Relations */
      for (i = 0; atk_relations_list[i]; i++)
        {
          if ((property =
               glade_widget_get_property (widget, atk_relations_list[i])))
            glade_gtk_parse_atk_relation (property, atk_node);
          else
            g_warning ("Couldnt find atk relation %s", atk_relations_list[i]);
        }
    }
383
384
385
}

void
386
387
glade_gtk_widget_read_widget (GladeWidgetAdaptor * adaptor,
                              GladeWidget * widget, GladeXmlNode * node)
388
{
389
390
  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
    return;
391

392
393
  /* First chain up and read in all the normal properties.. */
  GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
394

395
396
  /* Read in accelerators */
  glade_gtk_read_accels (widget, node, TRUE);
397

398
399
  /* Read in atk props */
  glade_gtk_widget_read_atk_props (widget, node);
400
401
402
403

}

static void
404
405
406
glade_gtk_widget_write_atk_property (GladeProperty * property,
                                     GladeXmlContext * context,
                                     GladeXmlNode * node)
407
{
408
  GladeXmlNode *prop_node;
409
  GladePropertyClass *pclass;
410
  gchar *value;
411

412
413
414
  glade_property_get (property, &value);
  if (value && value[0])
    {
415
416
      pclass = glade_property_get_class (property);

417
418
      prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_PROPERTY);
      glade_xml_node_append_child (node, prop_node);
419

420
      glade_xml_node_set_property_string (prop_node,
421
                                          GLADE_TAG_NAME, glade_property_class_id (pclass));
422

423
      glade_xml_set_content (prop_node, value);
424

425
      if (glade_property_i18n_get_translatable (property))
426
427
428
        glade_xml_node_set_property_string (prop_node,
                                            GLADE_TAG_TRANSLATABLE,
                                            GLADE_XML_TAG_I18N_TRUE);
429

430
      if (glade_property_i18n_get_comment (property))
431
432
        glade_xml_node_set_property_string (prop_node,
                                            GLADE_TAG_COMMENT,
433
434
435
436
437
438
                                            glade_property_i18n_get_comment (property));

      if (glade_property_i18n_get_context (property))
        glade_xml_node_set_property_string (prop_node,
                                            GLADE_TAG_CONTEXT,
                                            glade_property_i18n_get_context (property));
439
    }
440
441
442
}

static void
443
444
445
glade_gtk_widget_write_atk_properties (GladeWidget * widget,
                                       GladeXmlContext * context,
                                       GladeXmlNode * node)
446
{
447
448
  GladeXmlNode *child_node, *object_node;
  GladeProperty *name_prop, *desc_prop;
449

450
451
452
  name_prop = glade_widget_get_property (widget, "AtkObject::accessible-name");
  desc_prop =
      glade_widget_get_property (widget, "AtkObject::accessible-description");
453

454
455
456
457
  /* Create internal child here if any of these properties are non-null */
  if (!glade_property_default (name_prop) ||
      !glade_property_default (desc_prop))
    {
458
      gchar *atkname = g_strdup_printf ("%s-atkobject", glade_widget_get_name (widget));
459

460
461
      child_node = glade_xml_node_new (context, GLADE_XML_TAG_CHILD);
      glade_xml_node_append_child (node, child_node);
462

463
464
465
      glade_xml_node_set_property_string (child_node,
                                          GLADE_XML_TAG_INTERNAL_CHILD,
                                          GLADE_TAG_A11Y_INTERNAL_NAME);
466

467
468
      object_node = glade_xml_node_new (context, GLADE_XML_TAG_WIDGET);
      glade_xml_node_append_child (child_node, object_node);
469

470
471
      glade_xml_node_set_property_string (object_node,
                                          GLADE_XML_TAG_CLASS, "AtkObject");
472

473
474
      glade_xml_node_set_property_string (object_node,
                                          GLADE_XML_TAG_ID, atkname);
475

476
477
478
479
480
481
482
      if (!glade_property_default (name_prop))
        glade_gtk_widget_write_atk_property (name_prop, context, object_node);
      if (!glade_property_default (desc_prop))
        glade_gtk_widget_write_atk_property (desc_prop, context, object_node);

      g_free (atkname);
    }
483
484
485

}

486
static void
487
488
489
490
491
glade_gtk_widget_write_atk_relation (GladeProperty * property,
                                     GladeXmlContext * context,
                                     GladeXmlNode * node)
{
  GladeXmlNode *prop_node;
492
  GladePropertyClass *pclass;
493
494
495
496
  gchar *value, **split;
  gint i;

  if ((value = glade_widget_adaptor_string_from_value
497
       (glade_property_class_get_adaptor (glade_property_get_class (property)),
498
        glade_property_get_class (property), glade_property_inline_value (property))) != NULL)
499
500
501
502
503
    {
      if ((split = g_strsplit (value, GPC_OBJECT_DELIMITER, 0)) != NULL)
        {
          for (i = 0; split[i] != NULL; i++)
            {
504
505
	      pclass = glade_property_get_class (property);

506
507
508
509
510
              prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_RELATION);
              glade_xml_node_append_child (node, prop_node);

              glade_xml_node_set_property_string (prop_node,
                                                  GLADE_TAG_A11Y_TYPE,
511
                                                  glade_property_class_id (pclass));
512
513
514
515
516
517
518
              glade_xml_node_set_property_string (prop_node,
                                                  GLADE_TAG_A11Y_TARGET,
                                                  split[i]);
            }
          g_strfreev (split);
        }
    }
519
520
521
}

static void
522
523
524
glade_gtk_widget_write_atk_relations (GladeWidget * widget,
                                      GladeXmlContext * context,
                                      GladeXmlNode * node)
525
{
526
527
  GladeProperty *property;
  gint i;
528

529
530
531
532
533
534
535
  for (i = 0; atk_relations_list[i]; i++)
    {
      if ((property =
           glade_widget_get_property (widget, atk_relations_list[i])))
        glade_gtk_widget_write_atk_relation (property, context, node);
      else
        g_warning ("Couldnt find atk relation %s on widget %s",
536
                   atk_relations_list[i], glade_widget_get_name (widget));
537
    }
538
539
540
}

static void
541
542
543
glade_gtk_widget_write_atk_action (GladeProperty * property,
                                   GladeXmlContext * context,
                                   GladeXmlNode * node)
544
{
545
  GladeXmlNode *prop_node;
546
  GladePropertyClass *pclass;
547
  gchar *value = NULL;
548

549
  glade_property_get (property, &value);
550

551
552
  if (value && value[0])
    {
553
      pclass = glade_property_get_class (property);
554
555
      prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_ACTION);
      glade_xml_node_append_child (node, prop_node);
556

557
558
      glade_xml_node_set_property_string (prop_node,
                                          GLADE_TAG_A11Y_ACTION_NAME,
559
                                          &glade_property_class_id (pclass)[4]);
560
561
562
      glade_xml_node_set_property_string (prop_node,
                                          GLADE_TAG_A11Y_DESC, value);
    }
563
564
565
}

static void
566
567
568
glade_gtk_widget_write_atk_actions (GladeWidget * widget,
                                    GladeXmlContext * context,
                                    GladeXmlNode * node)
569
{
570
  GladeProperty *property;
571

572
573
574
575
576
577
578
579
  if ((property = glade_widget_get_property (widget, "atk-click")) != NULL)
    glade_gtk_widget_write_atk_action (property, context, node);
  if ((property = glade_widget_get_property (widget, "atk-activate")) != NULL)
    glade_gtk_widget_write_atk_action (property, context, node);
  if ((property = glade_widget_get_property (widget, "atk-press")) != NULL)
    glade_gtk_widget_write_atk_action (property, context, node);
  if ((property = glade_widget_get_property (widget, "atk-release")) != NULL)
    glade_gtk_widget_write_atk_action (property, context, node);
580
581
582
}

static void
583
584
585
glade_gtk_widget_write_atk_props (GladeWidget * widget,
                                  GladeXmlContext * context,
                                  GladeXmlNode * node)
586
{
587
  GladeXmlNode *atk_node;
588

589
  atk_node = glade_xml_node_new (context, GLADE_TAG_A11Y_A11Y);
590

591
592
  glade_gtk_widget_write_atk_relations (widget, context, atk_node);
  glade_gtk_widget_write_atk_actions (widget, context, atk_node);
593

594
595
596
597
  if (!glade_xml_node_get_children (atk_node))
    glade_xml_node_delete (atk_node);
  else
    glade_xml_node_append_child (node, atk_node);
598

599
  glade_gtk_widget_write_atk_properties (widget, context, node);
600
601
602
}

static void
603
604
605
glade_gtk_write_accels (GladeWidget * widget,
                        GladeXmlContext * context,
                        GladeXmlNode * node, gboolean write_signal)
606
{
607
608
609
  GladeXmlNode *accel_node;
  GladeProperty *property;
  GList *list;
610

611
612
613
  /* Some child widgets may have disabled the property */
  if (!(property = glade_widget_get_property (widget, "accelerator")))
    return;
614

615
  for (list = g_value_get_boxed (glade_property_inline_value (property)); list; list = list->next)
616
617
    {
      GladeAccelInfo *accel = list->data;
618

619
620
621
      accel_node = glade_accel_write (accel, context, write_signal);
      glade_xml_node_append_child (node, accel_node);
    }
622
623
624
}

void
625
626
627
glade_gtk_widget_write_widget (GladeWidgetAdaptor * adaptor,
                               GladeWidget * widget,
                               GladeXmlContext * context, GladeXmlNode * node)
628
{
629
630
  if (!glade_xml_node_verify (node, GLADE_XML_TAG_WIDGET))
    return;
631

632
633
  /* First chain up and read in all the normal properties.. */
  GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
634

635
636
637
  /* The core takes care of signals */
  glade_gtk_write_accels (widget, context, node, TRUE);
  glade_gtk_widget_write_atk_props (widget, context, node);
638
639
640
641
}


GladeEditorProperty *
642
643
644
645
glade_gtk_widget_create_eprop (GladeWidgetAdaptor * adaptor,
                               GladePropertyClass * klass, gboolean use_command)
{
  GladeEditorProperty *eprop;
646
647
648
  GParamSpec          *pspec;

  pspec = glade_property_class_get_pspec (klass);
649
650

  /* chain up.. */
651
  if (pspec->value_type == GLADE_TYPE_ACCEL_GLIST)
652
653
654
655
656
657
658
    eprop = g_object_new (GLADE_TYPE_EPROP_ACCEL,
                          "property-class", klass,
                          "use-command", use_command, NULL);
  else
    eprop = GWA_GET_CLASS
        (G_TYPE_OBJECT)->create_eprop (adaptor, klass, use_command);
  return eprop;
659
660
661
}

gchar *
662
663
664
glade_gtk_widget_string_from_value (GladeWidgetAdaptor * adaptor,
                                    GladePropertyClass * klass,
                                    const GValue * value)
665
{
666
667
668
669
670
  GParamSpec          *pspec;

  pspec = glade_property_class_get_pspec (klass);

  if (pspec->value_type == GLADE_TYPE_ACCEL_GLIST)
671
672
673
674
    return glade_accels_make_string (g_value_get_boxed (value));
  else
    return GWA_GET_CLASS
        (G_TYPE_OBJECT)->string_from_value (adaptor, klass, value);
675
676
}

677
static void
678
679
widget_parent_changed (GtkWidget * widget,
                       GParamSpec * pspec, GladeWidgetAdaptor * adaptor)
680
{
681
  GladeWidget *gwidget = glade_widget_get_from_gobject (widget);
682
  GladeWidget *parent;
683

684
685
686
687
  /* this could get called for a stale instance of an object
   * being rebuilt for a contruct-only property. */
  if (!gwidget)
    return;
688

689
690
691
  parent = glade_widget_get_parent (gwidget);

  if (parent && !glade_widget_get_internal (parent))
692
693
694
    glade_widget_set_action_sensitive (gwidget, "remove_parent", TRUE);
  else
    glade_widget_set_action_sensitive (gwidget, "remove_parent", FALSE);
695
696
697
}

void
698
699
glade_gtk_widget_deep_post_create (GladeWidgetAdaptor * adaptor,
                                   GObject * widget, GladeCreateReason reason)
700
{
701
  GladeWidget *gwidget = glade_widget_get_from_gobject (widget);
702

703
704
705
706
  /* Work around bug 472555 by resetting the default event mask,
   * this way only user edits will be saved to the glade file. */
  if (reason == GLADE_CREATE_USER)
    glade_widget_property_reset (gwidget, "events");
707

708
  glade_widget_set_action_sensitive (gwidget, "remove_parent", FALSE);
709

710
  if (GWA_IS_TOPLEVEL (adaptor) || glade_widget_get_internal (gwidget))
711
712
713
    glade_widget_set_action_sensitive (gwidget, "add_parent", FALSE);

  /* Watch parents/projects and set actions sensitive/insensitive */
714
  if (!glade_widget_get_internal (gwidget))
715
716
    g_signal_connect (G_OBJECT (widget), "notify::parent",
                      G_CALLBACK (widget_parent_changed), adaptor);
717
718
}

719
void
720
721
722
glade_gtk_widget_set_property (GladeWidgetAdaptor * adaptor,
                               GObject * object,
                               const gchar * id, const GValue * value)
723
{
724
725
726
727
728
  /* FIXME: is this still needed with the new gtk+ tooltips? */
  if (!strcmp (id, "tooltip"))
    {
      id = "tooltip-text";
    }
729

730
  GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, id, value);
731
732
}

733
void
734
735
736
glade_gtk_widget_get_property (GladeWidgetAdaptor * adaptor,
                               GObject * object,
                               const gchar * id, GValue * value)
737
{
738
739
740
741
742
743
  if (!strcmp (id, "tooltip"))
    {
      id = "tooltip-text";
    }

  GWA_GET_CLASS (G_TYPE_OBJECT)->get_property (adaptor, object, id, value);
744
745
}

746
static GList *
747
748
749
750
751
752
753
create_command_property_list (GladeWidget * gnew, GList * saved_props)
{
  GList *l, *command_properties = NULL;

  for (l = saved_props; l; l = l->next)
    {
      GladeProperty *property = l->data;
754
      GladePropertyClass *pclass = glade_property_get_class (property);
755
      GladeProperty *orig_prop =
756
	glade_widget_get_pack_property (gnew, glade_property_class_id (pclass));
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
      GCSetPropData *pdata = g_new0 (GCSetPropData, 1);

      pdata->property = orig_prop;
      pdata->old_value = g_new0 (GValue, 1);
      pdata->new_value = g_new0 (GValue, 1);

      glade_property_get_value (orig_prop, pdata->old_value);
      glade_property_get_value (property, pdata->new_value);

      command_properties = g_list_prepend (command_properties, pdata);
    }
  return g_list_reverse (command_properties);
}


void
glade_gtk_widget_action_activate (GladeWidgetAdaptor * adaptor,
                                  GObject * object, const gchar * action_path)
{
  GladeWidget *gwidget = glade_widget_get_from_gobject (object), *gparent;
777
  GList this_widget = { 0, }, that_widget = { 0,};
778
779
780
781
782
783
784
785
  GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (object));
  GladeProject *project;

  if (parent)
    gparent = glade_widget_get_from_gobject (parent);
  else
    gparent = NULL;

786
787
  project = glade_widget_get_project (gwidget);

788
789
790
791
792
793
794
795
796
797
798
799
800
  if (strcmp (action_path, "preview") == 0)
    {
      glade_project_preview (project,
                             glade_widget_get_from_gobject ((gpointer) object));
    }
  else if (strcmp (action_path, "edit_separate") == 0)
    {
      GtkWidget *dialog = glade_editor_dialog_for_widget (gwidget);
      gtk_widget_show_all (dialog);
    }
  else if (strcmp (action_path, "remove_parent") == 0)
    {
      GladeWidget *new_gparent;
801
      GladeProperty *property;
802
803

      g_return_if_fail (gparent);
804
805

      property = glade_widget_get_parentless_widget_ref (gparent);
806
      new_gparent = glade_widget_get_parent (gparent);
807

808
      glade_command_push_group (_("Removing parent of %s"), glade_widget_get_name (gwidget));
809

810
811
      /* Remove "this" widget, If the parent we're removing is a parentless 
       * widget reference, the reference will be implicitly broken by the 'cut' command */
812
      this_widget.data = gwidget;
813
      glade_command_delete (&this_widget);
814
815
816
817
818

      /* Delete the parent */
      that_widget.data = gparent;
      glade_command_delete (&that_widget);

819
820
821
822
823
824
825
826
827
      /* Add "this" widget to the new parent, if there is no new parent this will re-add
       * the widget to the project at the toplevel without a parent
       */
      glade_command_add (&this_widget, new_gparent, NULL, project, FALSE);

      /* If the parent had a parentless widget reference, undoably add the child
       * as the new parentless widget reference here */
      if (property)
	glade_command_set_property (property, glade_widget_get_object (gwidget));
828
829
830
831
832
833

      glade_command_pop_group ();
    }
  else if (strncmp (action_path, "add_parent/", 11) == 0)
    {
      GType new_type = 0;
834
      GladeProperty *property;
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866

      if (strcmp (action_path + 11, "alignment") == 0)
        new_type = GTK_TYPE_ALIGNMENT;
      else if (strcmp (action_path + 11, "viewport") == 0)
        new_type = GTK_TYPE_VIEWPORT;
      else if (strcmp (action_path + 11, "eventbox") == 0)
        new_type = GTK_TYPE_EVENT_BOX;
      else if (strcmp (action_path + 11, "frame") == 0)
        new_type = GTK_TYPE_FRAME;
      else if (strcmp (action_path + 11, "aspect_frame") == 0)
        new_type = GTK_TYPE_ASPECT_FRAME;
      else if (strcmp (action_path + 11, "scrolled_window") == 0)
        new_type = GTK_TYPE_SCROLLED_WINDOW;
      else if (strcmp (action_path + 11, "expander") == 0)
        new_type = GTK_TYPE_EXPANDER;
      else if (strcmp (action_path + 11, "table") == 0)
        new_type = GTK_TYPE_TABLE;
      else if (strcmp (action_path + 11, "hbox") == 0)
        new_type = GTK_TYPE_HBOX;
      else if (strcmp (action_path + 11, "vbox") == 0)
        new_type = GTK_TYPE_VBOX;
      else if (strcmp (action_path + 11, "hpaned") == 0)
        new_type = GTK_TYPE_HPANED;
      else if (strcmp (action_path + 11, "vpaned") == 0)
        new_type = GTK_TYPE_VPANED;


      if (new_type)
        {
          GladeWidgetAdaptor *adaptor =
              glade_widget_adaptor_get_by_type (new_type);
          GList *saved_props, *prop_cmds;
867
	  GladeWidget *gnew_parent;
868
869
870
871
872
873
874
875

          /* Dont add non-scrollable widgets to scrolled windows... */
          if (gparent &&
              glade_util_check_and_warn_scrollable (gparent, adaptor,
                                                    glade_app_get_window ()))
            return;

          glade_command_push_group (_("Adding parent %s for %s"),
876
877
                                    glade_widget_adaptor_get_title (adaptor), 
				    glade_widget_get_name (gwidget));
878
879
880

          /* Record packing properties */
          saved_props =
881
882
	    glade_widget_dup_properties (gwidget, glade_widget_get_packing_properties (gwidget),
					 FALSE, FALSE, FALSE);
883

884
885
886
887
888

	  property = glade_widget_get_parentless_widget_ref (gwidget);

	  /* Remove "this" widget, If the parent we're removing is a parentless 
	   * widget reference, the reference will be implicitly broken by the 'cut' command */
889
          this_widget.data = gwidget;
890
          glade_command_delete (&this_widget);
891
892

          /* Create new widget and put it where the placeholder was */
893
          if ((gnew_parent =
894
895
               glade_command_create (adaptor, gparent, NULL, project)) != NULL)
            {
896
897
898
899
	      /* Now we created the new parent, if gwidget had a parentless widget reference...
	       * set that reference to the new parent instead */
	      if (property)
		glade_command_set_property (property, glade_widget_get_object (gnew_parent));
900
901
902
903

              /* Remove the alignment that we added in the frame's post_create... */
              if (new_type == GTK_TYPE_FRAME)
                {
904
                  GObject *frame = glade_widget_get_object (gnew_parent);
905
                  GladeWidget *galign =
906
                      glade_widget_get_from_gobject (gtk_bin_get_child (GTK_BIN (frame)));
907
908
909
910
911
912
913
914
                  GList to_delete = { 0, };

                  to_delete.data = galign;
                  glade_command_delete (&to_delete);
                }

              /* Create heavy-duty glade-command properties stuff */
              prop_cmds =
915
                  create_command_property_list (gnew_parent, saved_props);
916
917
918
919
920
              g_list_foreach (saved_props, (GFunc) g_object_unref, NULL);
              g_list_free (saved_props);

              /* Apply the properties in an undoable way */
              if (prop_cmds)
921
922
                glade_command_set_properties_list 
		  (glade_widget_get_project (gparent), prop_cmds);
923
924

              /* Add "this" widget to the new parent */
925
              glade_command_add (&this_widget, gnew_parent, NULL, project, FALSE);
926
927
            }
          else
928
929
930
931
932
933
934
935
	    {
	      /* Create parent was cancelled, paste back to parent */
	      glade_command_add (&this_widget, gparent, NULL, project, FALSE);

	      /* Restore any parentless widget reference if there was one */
	      if (property)
		glade_command_set_property (property, glade_widget_get_object (gwidget));
	    }
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958

          glade_command_pop_group ();
        }
    }
  else if (strcmp (action_path, "sizegroup_add") == 0)
    {
      /* Ignore dummy */
    }
  else
    GWA_GET_CLASS (G_TYPE_OBJECT)->action_activate (adaptor,
                                                    object, action_path);
}

static GList *
list_sizegroups (GladeWidget * gwidget)
{
  GladeProject *project = glade_widget_get_project (gwidget);
  GList *groups = NULL;
  const GList *list;

  for (list = glade_project_get_objects (project); list; list = list->next)
    {
      GladeWidget *iter = glade_widget_get_from_gobject (list->data);
959
      if (GTK_IS_SIZE_GROUP (glade_widget_get_object (iter)))
960
961
962
        groups = g_list_prepend (groups, iter);
    }
  return g_list_reverse (groups);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
963
964
965
}

static void
966
glade_gtk_widget_add2group_cb (GtkMenuItem * item, GladeWidget * gwidget)
Tristan Van Berkom's avatar
Tristan Van Berkom committed
967
{
968
969
970
971
972
973
  GladeWidget *group =
      g_object_get_data (G_OBJECT (item), "glade-group-widget");
  GladeWidgetAdaptor *adaptor =
      glade_widget_adaptor_get_by_type (GTK_TYPE_SIZE_GROUP);
  GList *widget_list = NULL, *new_list;
  GladeProperty *property;
Tristan Van Berkom's avatar
Tristan Van Berkom committed
974

975
  if (group)
976
977
978
    glade_command_push_group (_("Adding %s to Size Group %s"), 
			      glade_widget_get_name (gwidget),
                              glade_widget_get_name (group));
979
980
  else
    glade_command_push_group (_("Adding %s to a new Size Group"),
981
                              glade_widget_get_name (gwidget));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
982

983
984
985
986
987
  if (!group)
    /* Cant cancel a size group */
    group =
        glade_command_create (adaptor, NULL, NULL,
                              glade_widget_get_project (gwidget));
Tristan Van Berkom's avatar
Tristan Van Berkom committed
988

989
990
991
  property = glade_widget_get_property (group, "widgets");
  glade_property_get (property, &widget_list);
  new_list = g_list_copy (widget_list);
992
993
  if (!g_list_find (widget_list, glade_widget_get_object (gwidget)))
    new_list = g_list_append (new_list, glade_widget_get_object (gwidget));
994
  glade_command_set_property (property, new_list);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
995

996
  g_list_free (new_list);
Tristan Van Berkom's avatar
Tristan Van Berkom committed
997

998
  glade_command_pop_group ();
Tristan Van Berkom's avatar
Tristan Van Berkom committed
999
1000
1001
}


1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
GtkWidget *
glade_gtk_widget_action_submenu (GladeWidgetAdaptor * adaptor,
                                 GObject * object, const gchar * action_path)
{
  GladeWidget *gwidget = glade_widget_get_from_gobject (object);
  GList *groups, *list;

  if (strcmp (action_path, "sizegroup_add") == 0)
    {
      GtkWidget *menu = gtk_menu_new ();
      GtkWidget *separator, *item;
      GladeWidget *group;

      if ((groups = list_sizegroups (gwidget)) != NULL)
        {
          for (list = groups; list; list = list->next)
            {
              group = list->data;
1020
              item = gtk_menu_item_new_with_label (glade_widget_get_name (group));
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035

              g_object_set_data (G_OBJECT (item), "glade-group-widget", group);
              g_signal_connect (G_OBJECT (item), "activate",
                                G_CALLBACK (glade_gtk_widget_add2group_cb),
                                gwidget);

              gtk_widget_show (item);
              gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
            }
          g_list_free (groups);

          separator = gtk_menu_item_new ();
          gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
          gtk_widget_show (separator);
        }
1036

1037
1038
1039
1040
      /* Add trailing new... item */
      item = gtk_menu_item_new_with_label (_("New Size Group"));
      g_signal_connect (G_OBJECT (item), "activate",
                        G_CALLBACK (glade_gtk_widget_add2group_cb), gwidget);
Jose Maria Celorio's avatar
Jose Maria Celorio committed
1041

1042
1043
      gtk_widget_show (item);
      gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1044

1045
1046
1047
1048
1049
      return menu;
    }
  else if (GWA_GET_CLASS (G_TYPE_OBJECT)->action_submenu)
    return GWA_GET_CLASS (G_TYPE_OBJECT)->action_submenu (adaptor,
                                                          object, action_path);
1050

1051
1052
  return NULL;
}
1053

1054

1055

1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
/* ----------------------------- GtkContainer ------------------------------ */
void
glade_gtk_container_post_create (GladeWidgetAdaptor * adaptor,
                                 GObject * container, GladeCreateReason reason)
{
  GList *children;
  g_return_if_fail (GTK_IS_CONTAINER (container));

  if (reason == GLADE_CREATE_USER)
    {
      if ((children =
           gtk_container_get_children (GTK_CONTAINER (container))) == NULL)
        gtk_container_add (GTK_CONTAINER (container), glade_placeholder_new ());
      else
        g_list_free (children);
    }
}

1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
gboolean
glade_gtk_container_add_verify (GladeWidgetAdaptor *adaptor,
				GtkWidget          *container,
				GtkWidget          *child,
				gboolean            user_feedback)
{
  GladeWidget *gwidget = glade_widget_get_from_gobject (container);

  if (GTK_IS_WINDOW (child))
    {
      if (user_feedback)
	glade_util_ui_message (glade_app_get_window (),
			       GLADE_UI_INFO, NULL,
			       _("Cannot add a toplevel window to a containter."));

      return FALSE;
    }
  else if (!GTK_IS_WIDGET (child) ||
	   GTK_IS_TOOL_ITEM (child) ||
	   GTK_IS_MENU_ITEM (child))
    {
      if (user_feedback)
	glade_util_ui_message (glade_app_get_window (),
			       GLADE_UI_INFO, NULL,
			       _("Only widgets can be added to a %s."),
			       glade_widget_adaptor_get_title (adaptor));

      return FALSE;
    }
  else if (GWA_USE_PLACEHOLDERS (adaptor) &&
	   glade_util_count_placeholders (gwidget) == 0)
    {
      if (user_feedback)
	glade_util_ui_message (glade_app_get_window (),
			       GLADE_UI_INFO, NULL,
			       _("This %s has no placeholders available to add children."),
			       glade_widget_adaptor_get_title (adaptor));

      return FALSE;
    }

  return TRUE;
}

1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
void
glade_gtk_container_replace_child (GladeWidgetAdaptor * adaptor,
                                   GtkWidget * container,
                                   GtkWidget * current, GtkWidget * new_widget)
{
  GParamSpec **param_spec;
  GladePropertyClass *pclass;
  GValue *value;
  guint nproperties;
  guint i;

  if (gtk_widget_get_parent (current) != container)
    return;

  param_spec = gtk_container_class_list_child_properties
      (G_OBJECT_GET_CLASS (container), &nproperties);
  value = g_malloc0 (sizeof (GValue) * nproperties);

  for (i = 0; i < nproperties; i++)
    {
      g_value_init (&value[i], param_spec[i]->value_type);
      gtk_container_child_get_property
          (GTK_CONTAINER (container), current, param_spec[i]->name, &value[i]);
    }

  gtk_container_remove (GTK_CONTAINER (container), current);
  gtk_container_add (GTK_CONTAINER (container), new_widget);

  for (i = 0; i < nproperties; i++)
    {
      /* If the added widget is a placeholder then we
       * want to keep all the "tranfer-on-paste" properties
       * as default so that it looks fresh (transfer-on-paste
       * properties dont effect the position/slot inside a 
       * contianer)
       */
      if (GLADE_IS_PLACEHOLDER (new_widget))
        {
          pclass = glade_widget_adaptor_get_pack_property_class
              (adaptor, param_spec[i]->name);

1159
          if (pclass && glade_property_class_transfer_on_paste (pclass))
1160
1161
            continue;
        }
1162

1163
1164
1165
1166
      gtk_container_child_set_property
          (GTK_CONTAINER (container), new_widget, param_spec[i]->name,
           &value[i]);
    }
1167

1168
1169
  for (i = 0; i < nproperties; i++)
    g_value_unset (&value[i]);
1170

1171
1172
  g_free (param_spec);
  g_free (value);
1173
1174
}

1175
void
1176
1177
glade_gtk_container_add_child (GladeWidgetAdaptor * adaptor,
                               GtkWidget * container, GtkWidget * child)
1178
{
1179
  GtkWidget *container_child = NULL;
1180

1181
1182
  if (GTK_IS_BIN (container))
    container_child = gtk_bin_get_child (GTK_BIN (container));
1183

1184
1185
1186
1187
1188
  /* Get a placeholder out of the way before adding the child if its a GtkBin
   */
  if (GTK_IS_BIN (container) && container_child != NULL &&
      GLADE_IS_PLACEHOLDER (container_child))
    gtk_container_remove (GTK_CONTAINER (container), container_child);
1189