glade-signal.c 16.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (C) 2001 Ximian, Inc.
 *
 * 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
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 18 19
 *
 * Authors:
 *   Chema Celorio <chema@celorio.com>
20
 *   Paolo Borelli <pborelli@katamail.com>
21 22
 */

Christian Persch's avatar
Christian Persch committed
23 24
#include "config.h"

25 26
#include <string.h>

Christian Persch's avatar
Christian Persch committed
27 28
#include <glib/gi18n-lib.h>

29 30
#include "glade.h"
#include "glade-signal.h"
31 32
#include "glade-xml-utils.h"

33
struct _GladeSignalPrivate
34
{
35 36
  const GladeSignalClass *class;   /* Pointer to the signal class */
  gchar    *detail;       /* Signal detail */
37 38 39 40 41 42 43 44 45
  gchar    *handler;      /* Handler function eg "gtk_main_quit" */
  gchar    *userdata;     /* User data signal handler argument   */

  gchar    *support_warning;/* Message to inform the user about signals introduced in future versions */

  guint8    after : 1;    /* Connect after TRUE or FALSE         */
  guint8    swapped : 1;  /* Connect swapped TRUE or FALSE (GtkBuilder only) */
};

46 47
enum {
  PROP_0,
48
  PROP_CLASS,
49
  PROP_DETAIL,
50 51
  PROP_HANDLER,
  PROP_USERDATA,
52
  PROP_SUPPORT_WARNING,
53
  PROP_AFTER,
54 55
  PROP_SWAPPED,
  N_PROPERTIES
56 57
};

58 59 60 61 62 63
/* We need these defines because GladeSignalClass is another object type!
 * So we use GladeSignalKlass as the class name for GladeSignal
 */
#define GladeSignalClass GladeSignalKlass
#define glade_signal_class_init glade_signal_klass_init

64
G_DEFINE_TYPE_WITH_PRIVATE (GladeSignal, glade_signal, G_TYPE_OBJECT)
65

66 67 68 69
#undef GladeSignalClass
#undef glade_signal_class_init

static GParamSpec *properties[N_PROPERTIES];
70 71 72 73 74 75

static void
glade_signal_finalize (GObject *object)
{
  GladeSignal *signal = GLADE_SIGNAL (object);

76
  g_free (signal->priv->detail);
77 78 79 80
  g_free (signal->priv->handler);
  g_free (signal->priv->userdata);
  g_free (signal->priv->support_warning);

81
  G_OBJECT_CLASS (glade_signal_parent_class)->finalize (object);
82 83 84
}

static void
85 86 87 88
glade_signal_get_property (GObject    *object,
                           guint       prop_id,
                           GValue     *value,
                           GParamSpec *pspec)
89 90 91 92 93
{
  GladeSignal *signal = GLADE_SIGNAL (object);

  switch (prop_id)
    {
94 95
      case PROP_CLASS:
        g_value_set_pointer (value, (gpointer) signal->priv->class);
96
        break;
97 98 99
      case PROP_DETAIL:
        g_value_set_string (value, signal->priv->detail);
        break;
100 101 102 103 104 105
      case PROP_HANDLER:
        g_value_set_string (value, signal->priv->handler);
        break;
      case PROP_USERDATA:
        g_value_set_string (value, signal->priv->userdata);
        break; 
106
      case PROP_SUPPORT_WARNING:
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
        g_value_set_string (value, signal->priv->support_warning);
        break; 
      case PROP_AFTER:
        g_value_set_boolean (value, signal->priv->after);
        break; 
      case PROP_SWAPPED:
        g_value_set_boolean (value, signal->priv->swapped);
        break; 
     default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
122 123 124 125
glade_signal_set_property (GObject      *object,
                           guint         prop_id,
                           const GValue *value,
                           GParamSpec   *pspec)
126 127 128 129 130
{
  GladeSignal *signal = GLADE_SIGNAL (object);

  switch (prop_id)
    {
131
      case PROP_CLASS:
132
        signal->priv->class = g_value_get_pointer (value);
133
        break;
134
      case PROP_DETAIL:
135
        glade_signal_set_detail (signal, g_value_get_string (value));
136
        break;
137
      case PROP_HANDLER:
138
        glade_signal_set_handler (signal, g_value_get_string (value));
139 140
        break;
      case PROP_USERDATA:
141
        glade_signal_set_userdata (signal, g_value_get_string (value));
142
        break; 
143
      case PROP_SUPPORT_WARNING:
144
        glade_signal_set_support_warning (signal, g_value_get_string (value));
145 146
        break; 
      case PROP_AFTER:
147
        glade_signal_set_after (signal, g_value_get_boolean (value));
148 149
        break; 
      case PROP_SWAPPED:
150
        glade_signal_set_swapped (signal, g_value_get_boolean (value));
151 152 153 154 155 156 157 158 159 160
        break; 
      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
        break;
    }
}

static void
glade_signal_init (GladeSignal *signal)
{
161
  signal->priv = glade_signal_get_instance_private (signal);
162 163 164 165 166 167 168 169
}

static void
glade_signal_klass_init (GladeSignalKlass *klass)
{
  GObjectClass *object_class;

  object_class = G_OBJECT_CLASS (klass);
170
  glade_signal_parent_class = g_type_class_peek_parent (klass);
171 172 173 174 175 176

  object_class->set_property = glade_signal_set_property;
  object_class->get_property = glade_signal_get_property;
  object_class->finalize     = glade_signal_finalize;

  /* Properties */
177 178 179 180 181 182
  properties[PROP_CLASS] =
    g_param_spec_pointer ("class",
                          _("SignalClass"),
                          _("The signal class of this signal"),
                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);

183 184 185 186 187 188
  properties[PROP_DETAIL] =
    g_param_spec_string ("detail",
                         _("Detail"),
                         _("The detail for this signal"),
                         NULL, G_PARAM_READWRITE);
  
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
  properties[PROP_HANDLER] =
    g_param_spec_string ("handler",
                         _("Handler"),
                         _("The handler for this signal"),
                         NULL, G_PARAM_READWRITE);

  properties[PROP_USERDATA] =
    g_param_spec_string ("userdata",
                         _("User Data"),
                         _("The user data for this signal"),
                         NULL, G_PARAM_READWRITE);

  properties[PROP_SUPPORT_WARNING] =
    g_param_spec_string ("support-warning",
                         _("Support Warning"),
                         _("The versioning support warning for this signal"),
                         NULL, G_PARAM_READWRITE);

  properties[PROP_AFTER] =
    g_param_spec_boolean ("after",
                          _("After"),
                          _("Whether this signal is run after default handlers"),
                          FALSE, G_PARAM_READWRITE);

  properties[PROP_SWAPPED] =
    g_param_spec_boolean ("swapped",
                          _("Swapped"),
                          _("Whether the user data is swapped with the instance for the handler"),
                          FALSE, G_PARAM_READWRITE);
  
  /* Install all properties */
  g_object_class_install_properties (object_class, N_PROPERTIES, properties);
221 222
}

223 224
/**
 * glade_signal_new:
225
 * @sig_class: a #GladeSignalClass
226
 * @handler: a handler function for the signal
227
 * @userdata: the userdata for this signal
228 229
 * @after: whether this handler should be called after the default emission phase
 * @swapped: whether the handler's user data should be swapped with the emitter instance.
230 231 232 233 234
 *
 * Creates a new #GladeSignal with the given parameters.
 *
 * Returns: the new #GladeSignal
 */
235
GladeSignal *
236
glade_signal_new (const GladeSignalClass *sig_class,
237 238
                  const gchar            *handler,
                  const gchar            *userdata, 
239 240
                  gboolean                after, 
                  gboolean                swapped)
241
{
242
  g_return_val_if_fail (sig_class != NULL, NULL);
243

244 245 246 247 248 249 250
  return GLADE_SIGNAL (g_object_new (GLADE_TYPE_SIGNAL,
                                     "class", sig_class,
                                     "handler", handler,
                                     "userdata", userdata,
                                     "after", after,
                                     "swapped", swapped,
                                     NULL));
251
}
252

253 254 255 256 257 258 259
/**
 * glade_signal_equal:
 * @sig1: a #GladeSignal
 * @sig2: a #GladeSignal
 *
 * Returns: %TRUE if @sig1 and @sig2 have identical attributes, %FALSE otherwise
 */
260
gboolean
261
glade_signal_equal (const GladeSignal *sig1, const GladeSignal *sig2)
262
{
263
  gboolean ret = FALSE;
264

265 266 267 268
  g_return_val_if_fail (GLADE_IS_SIGNAL (sig1), FALSE);
  g_return_val_if_fail (GLADE_IS_SIGNAL (sig2), FALSE);

  /* Intentionally ignore support_warning */
269 270 271
  if (!g_strcmp0 (glade_signal_get_name (sig1), glade_signal_get_name (sig2)) &&
      !g_strcmp0 (sig1->priv->handler, sig2->priv->handler) &&
      !g_strcmp0 (sig1->priv->detail, sig2->priv->detail) &&
272
      sig1->priv->after == sig2->priv->after && sig1->priv->swapped == sig2->priv->swapped)
273
    {
274 275
      if ((sig1->priv->userdata == NULL && sig2->priv->userdata == NULL) ||
          (sig1->priv->userdata != NULL && sig2->priv->userdata != NULL &&
276
           !g_strcmp0 (sig1->priv->userdata, sig2->priv->userdata)))
277 278 279 280
        ret = TRUE;
    }

  return ret;
281 282
}

283 284 285 286 287 288
/**
 * glade_signal_clone:
 * @signal: a #GladeSignal
 *
 * Returns: a new #GladeSignal with the same attributes as @signal
 */
289
GladeSignal *
290
glade_signal_clone (const GladeSignal *signal)
291
{
292 293 294
  GladeSignal *dup;

  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);
295

296
  dup = glade_signal_new (signal->priv->class,
297 298
                          signal->priv->handler,
                          signal->priv->userdata, 
299 300
                          signal->priv->after, 
                          signal->priv->swapped);
301

302
  glade_signal_set_detail (dup, signal->priv->detail);
303
  glade_signal_set_support_warning (dup, signal->priv->support_warning);
304

305
  return dup;
306 307
}

308 309
/**
 * glade_signal_write:
310 311 312
 * @signal: The #GladeSignal
 * @context: A #GladeXmlContext
 * @node: A #GladeXmlNode
313
 *
314
 * Writes @signal to @node
315
 */
316
void
317
glade_signal_write (GladeSignal     *signal,
318
                    GladeXmlContext *context,
319
                    GladeXmlNode    *node)
320
{
321 322 323 324 325 326 327
  GladeXmlNode *signal_node;
  gchar *name;

  /*  Should assert GLADE_XML_TAG_WIDGET tag here, but no 
   * access to project, so not really seriosly needed 
   */

328 329 330 331 332 333
  if (signal->priv->detail)
    name = g_strdup_printf ("%s::%s",
                            glade_signal_get_name (signal),
                            signal->priv->detail);
  else
    name = g_strdup (glade_signal_get_name (signal));
334 335 336 337 338 339 340

  /* Now dump the node values... */
  signal_node = glade_xml_node_new (context, GLADE_XML_TAG_SIGNAL);
  glade_xml_node_append_child (node, signal_node);

  glade_xml_node_set_property_string (signal_node, GLADE_XML_TAG_NAME, name);
  glade_xml_node_set_property_string (signal_node, GLADE_XML_TAG_HANDLER,
341
                                      signal->priv->handler);
342

343
  if (signal->priv->userdata)
344
    glade_xml_node_set_property_string (signal_node,
345
                                        GLADE_XML_TAG_OBJECT, signal->priv->userdata);
346

347
  if (signal->priv->after)
348 349 350 351 352 353 354 355 356
    glade_xml_node_set_property_string (signal_node,
                                        GLADE_XML_TAG_AFTER,
                                        GLADE_XML_TAG_SIGNAL_TRUE);

  /* Always serialize swapped regardless of format (libglade should not complain about this
   * and we prefer to not lose data in conversions).
   */
  glade_xml_node_set_property_string (signal_node,
                                      GLADE_XML_TAG_SWAPPED,
357
                                      signal->priv->swapped ?
358 359 360 361
                                      GLADE_XML_TAG_SIGNAL_TRUE :
                                      GLADE_XML_TAG_SIGNAL_FALSE);

  g_free (name);
362
}
363

364 365 366 367

/**
 * glade_signal_read:
 * @node: The #GladeXmlNode to read
368
 * @adaptor: The #GladeWidgetAdaptor for thw widget
369 370 371 372
 *
 * Reads and creates a ner #GladeSignal based on @node
 *
 * Returns: A newly created #GladeSignal
373
 */
374
GladeSignal *
375
glade_signal_read (GladeXmlNode *node, GladeWidgetAdaptor *adaptor)
376
{
377 378
  GladeSignal *signal = NULL;
  GladeSignalClass *signal_class;
379
  gchar *name, *handler, *userdata, *detail;
380 381 382 383 384 385 386 387 388 389

  g_return_val_if_fail (glade_xml_node_verify_silent
                        (node, GLADE_XML_TAG_SIGNAL), NULL);

  if (!(name =
        glade_xml_get_property_string_required (node, GLADE_XML_TAG_NAME,
                                                NULL)))
    return NULL;
  glade_util_replace (name, '_', '-');

390 391 392
  /* Search for a detail, and strip it from the signal name */
  if ((detail = g_strstr_len (name, -1, "::"))) *detail = '\0';
  
393 394 395 396 397 398 399 400
  if (!(handler =
        glade_xml_get_property_string_required (node, GLADE_XML_TAG_HANDLER,
                                                NULL)))
    {
      g_free (name);
      return NULL;
    }

401 402 403 404
  userdata     = glade_xml_get_property_string (node, GLADE_XML_TAG_OBJECT);
  signal_class = glade_widget_adaptor_get_signal_class (adaptor, name);

  if (signal_class)
405 406 407 408 409 410 411 412 413
    {
      signal = glade_signal_new (signal_class,
                                 handler, userdata,
                                 glade_xml_get_property_boolean (node, GLADE_XML_TAG_AFTER, FALSE),
                                 glade_xml_get_property_boolean (node, GLADE_XML_TAG_SWAPPED,
                                                                 userdata != NULL));

      if (detail && detail[2]) glade_signal_set_detail (signal, &detail[2]);
    }
414 415 416 417
  else
    {
      /* XXX These errors should be collected and reported to the user */
      g_warning ("No signal %s was found for class %s, skipping\n", 
418
                 name, glade_widget_adaptor_get_name (adaptor));
419
    }
420

421 422 423
  g_free (name);
  g_free (handler);
  g_free (userdata);
424 425

  return signal;
426
}
427

428
G_CONST_RETURN gchar *
429
glade_signal_get_name (const GladeSignal *signal)
430 431 432
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);

433 434 435 436
  return glade_signal_class_get_name (signal->priv->class);
}

G_CONST_RETURN GladeSignalClass *
437
glade_signal_get_class (const GladeSignal *signal)
438
{
439
        return signal->priv->class;
440 441 442
}

void
443 444 445 446 447 448 449 450
glade_signal_set_detail (GladeSignal *signal, const gchar *detail)
{
  g_return_if_fail (GLADE_IS_SIGNAL (signal));
  
  if (glade_signal_class_get_flags (signal->priv->class) & G_SIGNAL_DETAILED &&
      g_strcmp0 (signal->priv->detail, detail))
    {
      g_free (signal->priv->detail);
451
      signal->priv->detail = (detail && g_utf8_strlen (detail, -1)) ? g_strdup (detail) : NULL;
452 453 454 455 456 457 458 459 460 461 462 463 464 465
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_DETAIL]);
    }
}

G_CONST_RETURN gchar *
glade_signal_get_detail (const GladeSignal *signal)
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);

  return signal->priv->detail;
}

void
glade_signal_set_handler (GladeSignal *signal, const gchar *handler)
466 467 468
{
  g_return_if_fail (GLADE_IS_SIGNAL (signal));

469
  if (g_strcmp0 (signal->priv->handler, handler))
470
    {
471 472
      g_free (signal->priv->handler);
      signal->priv->handler =
473
          handler ? g_strdup (handler) : NULL;
474

475
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_HANDLER]);
476 477 478 479
    }
}

G_CONST_RETURN gchar *
480
glade_signal_get_handler (const GladeSignal *signal)
481 482 483
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);

484
  return signal->priv->handler;
485 486 487
}

void
488
glade_signal_set_userdata (GladeSignal *signal, const gchar *userdata)
489 490 491
{
  g_return_if_fail (GLADE_IS_SIGNAL (signal));

492
  if (g_strcmp0 (signal->priv->userdata, userdata))
493
    {
494 495
      g_free (signal->priv->userdata);
      signal->priv->userdata =
496
          userdata ? g_strdup (userdata) : NULL;
497

498
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_USERDATA]);
499 500 501 502
    }
}

G_CONST_RETURN gchar *
503
glade_signal_get_userdata (const GladeSignal *signal)
504 505 506
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);

507
  return signal->priv->userdata;
508 509 510
}

void
511
glade_signal_set_after (GladeSignal *signal, gboolean after)
512 513 514
{
  g_return_if_fail (GLADE_IS_SIGNAL (signal));

515 516 517 518
  if (signal->priv->after != after)
    {
      signal->priv->after = after;

519
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_AFTER]);
520
    }
521 522 523
}

gboolean
524
glade_signal_get_after (const GladeSignal *signal)
525 526 527
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), FALSE);

528
  return signal->priv->after;
529 530 531
}

void
532
glade_signal_set_swapped (GladeSignal *signal, gboolean swapped)
533 534 535
{
  g_return_if_fail (GLADE_IS_SIGNAL (signal));

536 537 538 539
  if (signal->priv->swapped != swapped)
    {
      signal->priv->swapped = swapped;

540
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_SWAPPED]);
541
    }
542 543 544
}

gboolean
545
glade_signal_get_swapped (const GladeSignal *signal)
546 547 548
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), FALSE);

549
  return signal->priv->swapped;
550 551 552 553 554
}

void
glade_signal_set_support_warning (GladeSignal *signal,
                                  const gchar *support_warning)
555
{
556 557
  g_return_if_fail (GLADE_IS_SIGNAL (signal));

558
  if (g_strcmp0 (signal->priv->support_warning, support_warning))
559
    {
560 561
      g_free (signal->priv->support_warning);
      signal->priv->support_warning =
562
          support_warning ? g_strdup (support_warning) : NULL;
563

564
      g_object_notify_by_pspec (G_OBJECT (signal), properties[PROP_SUPPORT_WARNING]);
565
    }
566
}
567 568

G_CONST_RETURN gchar *
569
glade_signal_get_support_warning (const GladeSignal *signal)
570 571 572
{
  g_return_val_if_fail (GLADE_IS_SIGNAL (signal), NULL);

573
  return signal->priv->support_warning;
574
}