NAIDuplicable: rewrite modification check stack
[nautilus-actions.git] / src / core / na-iduplicable.c
blob954bc45039ddce5a65c3ede15e91a93aaf19dc16
1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
24 * Authors:
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <api/na-iduplicable.h>
37 #include "na-marshal.h"
39 /* private interface data
41 struct _NAIDuplicableInterfacePrivate {
42 GList *consumers;
45 /* the data sructure set on each NAIDuplicable object
47 typedef struct {
48 NAIDuplicable *origin;
49 gboolean modified;
50 gboolean valid;
52 DuplicableStr;
54 #define NA_IDUPLICABLE_DATA_DUPLICABLE "na-iduplicable-data-duplicable"
56 /* signals emitted on NAIDuplicable when a status changes
58 enum {
59 MODIFIED_CHANGED,
60 VALID_CHANGED,
61 LAST_SIGNAL
64 static NAIDuplicableInterface *st_interface = NULL;
65 static gboolean st_initialized = FALSE;
66 static gboolean st_finalized = FALSE ;
67 static gint st_signals[ LAST_SIGNAL ] = { 0 };
69 static GType register_type( void );
70 static void interface_base_init( NAIDuplicableInterface *klass );
71 static void interface_base_finalize( NAIDuplicableInterface *klass );
73 static void v_copy( NAIDuplicable *target, const NAIDuplicable *source );
74 static gboolean v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b );
75 static gboolean v_is_valid( const NAIDuplicable *object );
77 static DuplicableStr *get_duplicable_str( const NAIDuplicable *object );
79 static void on_modified_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_modified );
80 static void on_valid_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_valid );
81 static void propagate_signal_to_consumers( NAIDuplicable *instance, const gchar *signal, GObject *object, gboolean new_status );
82 static void release_signal_consumers( GList *consumers );
84 GType
85 na_iduplicable_get_type( void )
87 static GType iface_type = 0;
89 if( !iface_type ){
90 iface_type = register_type();
93 return( iface_type );
96 static GType
97 register_type( void )
99 static const gchar *thisfn = "na_iduplicable_register_type";
100 GType type;
102 static const GTypeInfo info = {
103 sizeof( NAIDuplicableInterface ),
104 ( GBaseInitFunc ) interface_base_init,
105 ( GBaseFinalizeFunc ) interface_base_finalize,
106 NULL,
107 NULL,
108 NULL,
111 NULL
114 g_debug( "%s", thisfn );
116 type = g_type_register_static( G_TYPE_INTERFACE, "NAIDuplicable", &info, 0 );
118 g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
120 return( type );
123 static void
124 interface_base_init( NAIDuplicableInterface *klass )
126 static const gchar *thisfn = "na_iduplicable_interface_base_init";
128 if( !st_initialized ){
130 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
132 klass->private = g_new0( NAIDuplicableInterfacePrivate, 1 );
134 klass->private->consumers = NULL;
136 klass->copy = NULL;
137 klass->are_equal = NULL;
138 klass->is_valid = NULL;
141 * NAIDuplicable::modified-changed:
143 * This signal is emitted by #NAIDuplicable when the modification
144 * status of an object has been modified.
146 * The default class handler propagates the signal to registered
147 * consumers.
149 * Signal args: New modification status
151 * Handler prototype:
152 * void ( *handler )( NAIDuplicable *duplicable, NAObject *object, gboolean is_modified, gpointer user_data );
154 * When the signal is first emitted, thus on NAIDuplicable, @duplicable
155 * and @object are pointers to the same address. This duplication is
156 * relevant when propagating the signal to customer, as the signal is
157 * emitted on the customer itself, while we still need the @object
158 * pointer.
160 st_signals[ MODIFIED_CHANGED ] = g_signal_new_class_handler(
161 IDUPLICABLE_SIGNAL_MODIFIED_CHANGED,
162 G_TYPE_OBJECT,
163 G_SIGNAL_RUN_CLEANUP,
164 G_CALLBACK( on_modified_changed_class_handler ),
165 NULL,
166 NULL,
167 na_cclosure_marshal_VOID__POINTER_BOOLEAN,
168 G_TYPE_NONE,
170 G_TYPE_POINTER, G_TYPE_BOOLEAN );
173 * NAIDuplicable::valid-changed:
175 * This signal is emitted by #NAIDuplicable when the validity
176 * status of an object has been modified.
178 * The default class handler propagates the signal to registered
179 * consumers.
181 * Signal args: New validity status
183 * Handler prototype:
184 * void ( *handler )( NAIDuplicable *duplicable, NAObject *object, gboolean is_valid, gpointer user_data );
186 * When the signal is first emitted, thus on NAIDuplicable, @duplicable
187 * and @object are pointers to the same address. This duplication is
188 * relevant when propagating the signal to customer, as the signal is
189 * emitted on the customer itself, while we still need the @object
190 * pointer.
192 st_signals[ VALID_CHANGED ] = g_signal_new_class_handler(
193 IDUPLICABLE_SIGNAL_VALID_CHANGED,
194 G_TYPE_OBJECT,
195 G_SIGNAL_RUN_CLEANUP,
196 G_CALLBACK( on_valid_changed_class_handler ),
197 NULL,
198 NULL,
199 na_cclosure_marshal_VOID__POINTER_BOOLEAN,
200 G_TYPE_NONE,
202 G_TYPE_POINTER, G_TYPE_BOOLEAN );
204 st_interface = klass;
206 st_initialized = TRUE;
210 static void
211 interface_base_finalize( NAIDuplicableInterface *klass )
213 static const gchar *thisfn = "na_iduplicable_interface_base_finalize";
215 if( !st_finalized ){
217 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
219 st_finalized = TRUE;
221 release_signal_consumers( klass->private->consumers );
223 g_free( klass->private );
228 * na_iduplicable_dispose:
229 * @object: the #NAIDuplicable object to be initialized.
231 * Releases resources.
233 * Since: 2.30
235 void
236 na_iduplicable_dispose( const NAIDuplicable *object )
238 DuplicableStr *str;
240 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
242 if( st_initialized && !st_finalized ){
244 str = get_duplicable_str( object );
245 g_free( str );
246 g_object_set_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE, NULL );
251 * na_iduplicable_dump:
252 * @object: the #NAIDuplicable object to be dumped.
254 * Dumps via g_debug the properties of the object.
256 * We ouput here only the data we set ourselves againt the
257 * #NAIDuplicable -implemented object.
259 * This function should be called by the implementation when it dumps
260 * itself its own content.
262 * Since: 2.30
264 void
265 na_iduplicable_dump( const NAIDuplicable *object )
267 static const gchar *thisfn = "na_iduplicable_dump";
268 DuplicableStr *str;
270 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
272 if( st_initialized && !st_finalized ){
274 str = get_duplicable_str( object );
276 g_debug( "| %s: origin=%p", thisfn, ( void * ) str->origin );
277 g_debug( "| %s: modified=%s", thisfn, str->modified ? "True" : "False" );
278 g_debug( "| %s: valid=%s", thisfn, str->valid ? "True" : "False" );
283 * na_iduplicable_duplicate:
284 * @object: the #NAIDuplicable object to be duplicated.
286 * Exactly duplicates a #NAIDuplicable -implemented object, including
287 * modification and validity status which are copied from @object to
288 * the duplicated one.
290 * Though this function is not recursive by itself, it is widely supposed
291 * everywhere in the program that recursivity is provided but #NAObject
292 * implementation.
294 * <important>
295 * <para>
296 * na_object_duplicate() (aka na_iduplicable_duplicate())
297 * is definitively recursive
298 * </para>
299 * </important>
301 * Returns: a new #NAIDuplicable.
303 * Since: 2.30
305 NAIDuplicable *
306 na_iduplicable_duplicate( const NAIDuplicable *object )
308 static const gchar *thisfn = "na_iduplicable_duplicate";
309 NAIDuplicable *dup;
310 DuplicableStr *dup_str, *obj_str;
312 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
314 dup = NULL;
316 if( st_initialized && !st_finalized ){
318 g_debug( "%s: object=%p (%s)",
319 thisfn,
320 ( void * ) object, G_OBJECT_TYPE_NAME( object ));
322 dup = g_object_new( G_OBJECT_TYPE( object ), NULL );
324 v_copy( dup, object );
326 dup_str = get_duplicable_str( dup );
327 obj_str = get_duplicable_str( object );
329 dup_str->origin = ( NAIDuplicable * ) object;
330 dup_str->modified = obj_str->modified;
331 dup_str->valid = obj_str->valid;
334 return( dup );
338 * na_iduplicable_check_status:
339 * @object: the #NAIDuplicable object to be checked.
341 * Checks the edition status of the #NAIDuplicable object, and set up
342 * the corresponding properties.
344 * This function is supposed to be called each time the object may have
345 * been modified in order to set the corresponding properties. Helper
346 * functions na_iduplicable_is_modified() and na_iduplicable_is_valid()
347 * will then only return the current value of the properties.
349 * na_iduplicable_check_status() is not, as itself, recursive.
350 * That is, the modification and validity status are only set on the
351 * specified object.
352 * #NAObject implementation has chosen to handle itself the recursivity:
353 * na_object_check_status() so first check status for children, before
354 * calling this function.
356 * Since: 2.30
358 void
359 na_iduplicable_check_status( const NAIDuplicable *object )
361 static const gchar *thisfn = "na_iduplicable_check_status";
362 DuplicableStr *str;
363 gboolean was_modified, was_valid;
365 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
367 if( st_initialized && !st_finalized ){
368 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
370 str = get_duplicable_str( object );
372 was_modified = str->modified;
373 was_valid = str->valid;
375 if( str->origin ){
376 g_debug( "%s: vs. origin=%p (%s)", thisfn, ( void * ) str->origin, G_OBJECT_TYPE_NAME( str->origin ));
377 g_return_if_fail( NA_IS_IDUPLICABLE( str->origin ));
378 str->modified = !v_are_equal( str->origin, object );
380 } else {
381 str->modified = TRUE;
384 if( was_modified != str->modified ){
385 g_debug( "%s: %p (%s) status changed to modified=%s",
386 thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ), str->modified ? "True":"False" );
387 g_signal_emit_by_name( G_OBJECT( object ), IDUPLICABLE_SIGNAL_MODIFIED_CHANGED, object, str->modified );
390 str->valid = v_is_valid( object );
392 if( was_valid != str->valid ){
393 g_debug( "%s: %p (%s) status changed to valid=%s",
394 thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ), str->valid ? "True":"False" );
395 g_signal_emit_by_name( G_OBJECT( object ), IDUPLICABLE_SIGNAL_VALID_CHANGED, object, str->valid );
401 * na_iduplicable_get_origin:
402 * @object: the #NAIDuplicable object whose origin is to be returned.
404 * Returns the origin of a duplicated #NAIDuplicable.
406 * Returns: the original #NAIDuplicable, or NULL.
408 * Since: 2.30
410 NAIDuplicable *
411 na_iduplicable_get_origin( const NAIDuplicable *object )
413 NAIDuplicable *origin;
414 DuplicableStr *str;
416 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
418 origin = NULL;
420 if( st_initialized && !st_finalized ){
422 str = get_duplicable_str( object );
423 origin = str->origin;
426 return( origin );
430 * na_iduplicable_is_valid:
431 * @object: the #NAIDuplicable object whose status is to be returned.
433 * Returns the current value of the relevant property
434 * without rechecking the edition status itself.
436 * Returns: %TRUE is the provided object is valid.
438 * Since: 2.30
440 gboolean
441 na_iduplicable_is_valid( const NAIDuplicable *object )
443 gboolean is_valid;
444 DuplicableStr *str;
446 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), FALSE );
448 is_valid = FALSE;
450 if( st_initialized && !st_finalized ){
452 str = get_duplicable_str( object );
453 is_valid = str->valid;
456 return( is_valid );
460 * na_iduplicable_is_modified:
461 * @object: the #NAIDuplicable object whose status is to be returned.
463 * Returns the current value of the 'is_modified'
464 * property without rechecking the edition status itself.
466 * Returns: %TRUE is the provided object has been modified regarding of
467 * the original one.
469 * Since: 2.30
471 gboolean
472 na_iduplicable_is_modified( const NAIDuplicable *object )
474 gboolean is_modified;
475 DuplicableStr *str;
477 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), FALSE );
479 is_modified = FALSE;
481 if( st_initialized && !st_finalized ){
483 str = get_duplicable_str( object );
484 is_modified = str->modified;
487 return( is_modified );
491 * na_iduplicable_set_origin:
492 * @object: the #NAIDuplicable object whose origin is to be set.
493 * @origin: the new original #NAIDuplicable.
495 * Sets the new origin of a duplicated #NAIDuplicable.
497 * Since: 2.30
499 void
500 na_iduplicable_set_origin( NAIDuplicable *object, const NAIDuplicable *origin )
502 DuplicableStr *str;
504 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
505 g_return_if_fail( NA_IS_IDUPLICABLE( origin ) || !origin );
507 if( st_initialized && !st_finalized ){
509 str = get_duplicable_str( object );
510 str->origin = ( NAIDuplicable * ) origin;
514 #ifndef NA_DISABLE_DEPRECATED
516 * na_iduplicable_set_modified:
517 * @object: the #NAIDuplicable object whose modification status is to be set.
518 * @modified: the new modification status #NAIDuplicable.
520 * Sets the new modified of a duplicated #NAIDuplicable.
522 * Since: 2.30
523 * Deprecated: 3.1.0
525 void
526 na_iduplicable_set_modified( NAIDuplicable *object, gboolean modified )
528 DuplicableStr *str;
530 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
532 if( st_initialized && !st_finalized ){
534 str = get_duplicable_str( object );
535 str->modified = modified;
538 #endif /* NA_DISABLE_DEPRECATED */
540 static void
541 v_copy( NAIDuplicable *target, const NAIDuplicable *source )
543 if( NA_IDUPLICABLE_GET_INTERFACE( target )->copy ){
544 NA_IDUPLICABLE_GET_INTERFACE( target )->copy( target, source );
548 static gboolean
549 v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b )
551 if( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal ){
552 return( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal( a, b ));
555 return( TRUE );
558 static gboolean
559 v_is_valid( const NAIDuplicable *object )
561 if( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid ){
562 return( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid( object ));
565 return( TRUE );
569 * na_iduplicable_register_consumer:
570 * @consumer: the target instance.
572 * This function registers a consumer, i.e. an instance to which edition
573 * status signals will be propagated.
575 * Since: 2.30
577 void
578 na_iduplicable_register_consumer( GObject *consumer )
580 g_return_if_fail( st_interface );
582 if( st_initialized && !st_finalized ){
584 g_debug( "na_iduplicable_register_consumer: consumer=%p", ( void * ) consumer );
586 st_interface->private->consumers = g_list_prepend( st_interface->private->consumers, consumer );
590 static void
591 on_modified_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_modified )
593 if( NA_IS_IDUPLICABLE( instance )){
594 propagate_signal_to_consumers( instance, IDUPLICABLE_SIGNAL_MODIFIED_CHANGED, object, is_modified );
598 static void
599 on_valid_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_valid )
601 if( NA_IS_IDUPLICABLE( instance )){
602 propagate_signal_to_consumers( instance, IDUPLICABLE_SIGNAL_VALID_CHANGED, object, is_valid );
606 static void
607 propagate_signal_to_consumers( NAIDuplicable *instance, const gchar *signal, GObject *object, gboolean new_status )
609 static const gchar *thisfn = "na_iduplicable_propagate_signals_to_consumers";
610 GList *ic;
612 g_return_if_fail( st_interface );
614 if( st_initialized && !st_finalized ){
615 g_debug( "%s: instance=%p, signal=%s", thisfn, ( void * ) instance, signal );
617 for( ic = st_interface->private->consumers ; ic ; ic = ic->next ){
618 g_signal_emit_by_name( ic->data, signal, object, new_status );
623 static void
624 release_signal_consumers( GList *consumers )
626 g_list_free( consumers );
629 static DuplicableStr *
630 get_duplicable_str( const NAIDuplicable *object )
632 DuplicableStr *str;
634 str = ( DuplicableStr * ) g_object_get_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE );
636 if( !str ){
637 str = g_new0( DuplicableStr, 1 );
639 str->origin = NULL;
640 str->modified = FALSE;
641 str->valid = TRUE;
643 g_object_set_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE, str );
646 return( str );