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.
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)
35 #include <api/na-iduplicable.h>
37 #include "na-marshal.h"
39 /* private interface data
41 struct _NAIDuplicableInterfacePrivate
{
45 /* the data sructure set on each NAIDuplicable object
48 NAIDuplicable
*origin
;
54 #define NA_IDUPLICABLE_DATA_DUPLICABLE "na-iduplicable-data-duplicable"
56 /* signals emitted on NAIDuplicable when a status changes
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
);
85 na_iduplicable_get_type( void )
87 static GType iface_type
= 0;
90 iface_type
= register_type();
99 static const gchar
*thisfn
= "na_iduplicable_register_type";
102 static const GTypeInfo info
= {
103 sizeof( NAIDuplicableInterface
),
104 ( GBaseInitFunc
) interface_base_init
,
105 ( GBaseFinalizeFunc
) interface_base_finalize
,
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
);
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
;
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
149 * Signal args: New modification status
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
160 st_signals
[ MODIFIED_CHANGED
] = g_signal_new_class_handler(
161 IDUPLICABLE_SIGNAL_MODIFIED_CHANGED
,
163 G_SIGNAL_RUN_CLEANUP
,
164 G_CALLBACK( on_modified_changed_class_handler
),
167 na_cclosure_marshal_VOID__POINTER_BOOLEAN
,
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
181 * Signal args: New validity status
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
192 st_signals
[ VALID_CHANGED
] = g_signal_new_class_handler(
193 IDUPLICABLE_SIGNAL_VALID_CHANGED
,
195 G_SIGNAL_RUN_CLEANUP
,
196 G_CALLBACK( on_valid_changed_class_handler
),
199 na_cclosure_marshal_VOID__POINTER_BOOLEAN
,
202 G_TYPE_POINTER
, G_TYPE_BOOLEAN
);
204 st_interface
= klass
;
206 st_initialized
= TRUE
;
211 interface_base_finalize( NAIDuplicableInterface
*klass
)
213 static const gchar
*thisfn
= "na_iduplicable_interface_base_finalize";
217 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
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.
236 na_iduplicable_dispose( const NAIDuplicable
*object
)
240 g_return_if_fail( NA_IS_IDUPLICABLE( object
));
242 if( st_initialized
&& !st_finalized
){
244 str
= get_duplicable_str( object
);
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.
265 na_iduplicable_dump( const NAIDuplicable
*object
)
267 static const gchar
*thisfn
= "na_iduplicable_dump";
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
296 * na_object_duplicate() (aka na_iduplicable_duplicate())
297 * is definitively recursive
301 * Returns: a new #NAIDuplicable.
306 na_iduplicable_duplicate( const NAIDuplicable
*object
)
308 static const gchar
*thisfn
= "na_iduplicable_duplicate";
310 DuplicableStr
*dup_str
, *obj_str
;
312 g_return_val_if_fail( NA_IS_IDUPLICABLE( object
), NULL
);
316 if( st_initialized
&& !st_finalized
){
318 g_debug( "%s: object=%p (%s)",
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
;
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
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.
359 na_iduplicable_check_status( const NAIDuplicable
*object
)
361 static const gchar
*thisfn
= "na_iduplicable_check_status";
363 gboolean was_modified
, was_valid
;
365 g_return_if_fail( NA_IS_IDUPLICABLE( object
));
367 if( st_initialized
&& !st_finalized
){
369 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
371 str
= get_duplicable_str( object
);
373 was_modified
= str
->modified
;
374 was_valid
= str
->valid
;
377 g_debug( "%s: vs. origin=%p (%s)", thisfn
, ( void * ) str
->origin
, G_OBJECT_TYPE_NAME( str
->origin
));
378 g_return_if_fail( NA_IS_IDUPLICABLE( str
->origin
));
379 str
->modified
= !v_are_equal( str
->origin
, object
);
382 str
->modified
= TRUE
;
385 if( was_modified
!= str
->modified
){
386 g_debug( "%s: %p (%s) status changed to modified=%s",
387 thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
), str
->modified
? "True":"False" );
388 g_signal_emit_by_name( G_OBJECT( object
), IDUPLICABLE_SIGNAL_MODIFIED_CHANGED
, object
, str
->modified
);
391 str
->valid
= v_is_valid( object
);
393 if( was_valid
!= str
->valid
){
394 g_debug( "%s: %p (%s) status changed to valid=%s",
395 thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
), str
->valid
? "True":"False" );
396 g_signal_emit_by_name( G_OBJECT( object
), IDUPLICABLE_SIGNAL_VALID_CHANGED
, object
, str
->valid
);
402 * na_iduplicable_get_origin:
403 * @object: the #NAIDuplicable object whose origin is to be returned.
405 * Returns the origin of a duplicated #NAIDuplicable.
407 * Returns: the original #NAIDuplicable, or NULL.
412 na_iduplicable_get_origin( const NAIDuplicable
*object
)
414 NAIDuplicable
*origin
;
417 g_return_val_if_fail( NA_IS_IDUPLICABLE( object
), NULL
);
421 if( st_initialized
&& !st_finalized
){
423 str
= get_duplicable_str( object
);
424 origin
= str
->origin
;
431 * na_iduplicable_is_valid:
432 * @object: the #NAIDuplicable object whose status is to be returned.
434 * Returns the current value of the relevant property
435 * without rechecking the edition status itself.
437 * Returns: %TRUE is the provided object is valid.
442 na_iduplicable_is_valid( const NAIDuplicable
*object
)
447 g_return_val_if_fail( NA_IS_IDUPLICABLE( object
), FALSE
);
451 if( st_initialized
&& !st_finalized
){
453 str
= get_duplicable_str( object
);
454 is_valid
= str
->valid
;
461 * na_iduplicable_is_modified:
462 * @object: the #NAIDuplicable object whose status is to be returned.
464 * Returns the current value of the 'is_modified'
465 * property without rechecking the edition status itself.
467 * Returns: %TRUE is the provided object has been modified regarding of
473 na_iduplicable_is_modified( const NAIDuplicable
*object
)
475 gboolean is_modified
;
478 g_return_val_if_fail( NA_IS_IDUPLICABLE( object
), FALSE
);
482 if( st_initialized
&& !st_finalized
){
484 str
= get_duplicable_str( object
);
485 is_modified
= str
->modified
;
488 return( is_modified
);
492 * na_iduplicable_set_origin:
493 * @object: the #NAIDuplicable object whose origin is to be set.
494 * @origin: the new original #NAIDuplicable.
496 * Sets the new origin of a duplicated #NAIDuplicable.
501 na_iduplicable_set_origin( NAIDuplicable
*object
, const NAIDuplicable
*origin
)
505 g_return_if_fail( NA_IS_IDUPLICABLE( object
));
506 g_return_if_fail( NA_IS_IDUPLICABLE( origin
) || !origin
);
508 if( st_initialized
&& !st_finalized
){
510 str
= get_duplicable_str( object
);
511 str
->origin
= ( NAIDuplicable
* ) origin
;
515 #ifndef NA_DISABLE_DEPRECATED
517 * na_iduplicable_set_modified:
518 * @object: the #NAIDuplicable object whose modification status is to be set.
519 * @modified: the new modification status #NAIDuplicable.
521 * Sets the new modified of a duplicated #NAIDuplicable.
527 na_iduplicable_set_modified( NAIDuplicable
*object
, gboolean modified
)
531 g_return_if_fail( NA_IS_IDUPLICABLE( object
));
533 if( st_initialized
&& !st_finalized
){
535 str
= get_duplicable_str( object
);
536 str
->modified
= modified
;
539 #endif /* NA_DISABLE_DEPRECATED */
542 v_copy( NAIDuplicable
*target
, const NAIDuplicable
*source
)
544 if( NA_IDUPLICABLE_GET_INTERFACE( target
)->copy
){
545 NA_IDUPLICABLE_GET_INTERFACE( target
)->copy( target
, source
);
550 v_are_equal( const NAIDuplicable
*a
, const NAIDuplicable
*b
)
552 if( NA_IDUPLICABLE_GET_INTERFACE( a
)->are_equal
){
553 return( NA_IDUPLICABLE_GET_INTERFACE( a
)->are_equal( a
, b
));
560 v_is_valid( const NAIDuplicable
*object
)
562 if( NA_IDUPLICABLE_GET_INTERFACE( object
)->is_valid
){
563 return( NA_IDUPLICABLE_GET_INTERFACE( object
)->is_valid( object
));
570 * na_iduplicable_register_consumer:
571 * @consumer: the target instance.
573 * This function registers a consumer, i.e. an instance to which edition
574 * status signals will be propagated.
579 na_iduplicable_register_consumer( GObject
*consumer
)
581 g_return_if_fail( st_interface
);
583 if( st_initialized
&& !st_finalized
){
585 g_debug( "na_iduplicable_register_consumer: consumer=%p", ( void * ) consumer
);
587 st_interface
->private->consumers
= g_list_prepend( st_interface
->private->consumers
, consumer
);
592 on_modified_changed_class_handler( NAIDuplicable
*instance
, GObject
*object
, gboolean is_modified
)
594 if( NA_IS_IDUPLICABLE( instance
)){
595 propagate_signal_to_consumers( instance
, IDUPLICABLE_SIGNAL_MODIFIED_CHANGED
, object
, is_modified
);
600 on_valid_changed_class_handler( NAIDuplicable
*instance
, GObject
*object
, gboolean is_valid
)
602 if( NA_IS_IDUPLICABLE( instance
)){
603 propagate_signal_to_consumers( instance
, IDUPLICABLE_SIGNAL_VALID_CHANGED
, object
, is_valid
);
608 propagate_signal_to_consumers( NAIDuplicable
*instance
, const gchar
*signal
, GObject
*object
, gboolean new_status
)
610 static const gchar
*thisfn
= "na_iduplicable_propagate_signals_to_consumers";
613 g_return_if_fail( st_interface
);
615 if( st_initialized
&& !st_finalized
){
616 g_debug( "%s: instance=%p, signal=%s", thisfn
, ( void * ) instance
, signal
);
618 for( ic
= st_interface
->private->consumers
; ic
; ic
= ic
->next
){
619 g_signal_emit_by_name( ic
->data
, signal
, object
, new_status
);
625 release_signal_consumers( GList
*consumers
)
627 g_list_free( consumers
);
630 static DuplicableStr
*
631 get_duplicable_str( const NAIDuplicable
*object
)
635 str
= ( DuplicableStr
* ) g_object_get_data( G_OBJECT( object
), NA_IDUPLICABLE_DATA_DUPLICABLE
);
638 str
= g_new0( DuplicableStr
, 1 );
641 str
->modified
= FALSE
;
644 g_object_set_data( G_OBJECT( object
), NA_IDUPLICABLE_DATA_DUPLICABLE
, str
);