1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
4 *
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8 *
9 * Nautilus-Actions 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.
13 *
14 * Nautilus-Actions 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 GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Nautilus-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
22 *
23 * Authors:
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33
34 #include <api/na-iduplicable.h>
35
36 #include "na-marshal.h"
37
38 /* private interface data
39 */
40 struct _NAIDuplicableInterfacePrivate {
41 GList *consumers;
42 };
43
44 /* the data sructure set on each NAIDuplicable object
45 */
46 typedef struct {
47 NAIDuplicable *origin;
48 gboolean modified;
49 gboolean valid;
50 }
51 DuplicableStr;
52
53 #define NA_IDUPLICABLE_DATA_DUPLICABLE "na-iduplicable-data-duplicable"
54
55 /* signals emitted on NAIDuplicable when a status changes
56 */
57 enum {
58 MODIFIED_CHANGED,
59 VALID_CHANGED,
60 LAST_SIGNAL
61 };
62
63 static NAIDuplicableInterface *st_interface = NULL;
64 static guint st_initializations = 0;
65 static gint st_signals[ LAST_SIGNAL ] = { 0 };
66
67 static GType register_type( void );
68 static void interface_base_init( NAIDuplicableInterface *klass );
69 static void interface_base_finalize( NAIDuplicableInterface *klass );
70
71 static void v_copy( NAIDuplicable *target, const NAIDuplicable *source, guint mode );
72 static gboolean v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b );
73 static gboolean v_is_valid( const NAIDuplicable *object );
74
75 static DuplicableStr *get_duplicable_str( const NAIDuplicable *object );
76
77 static void on_modified_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_modified );
78 static void on_valid_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_valid );
79 static void propagate_signal_to_consumers( NAIDuplicable *instance, const gchar *signal, GObject *object, gboolean new_status );
80 static void release_signal_consumers( GList *consumers );
81
82 GType
na_iduplicable_get_type(void)83 na_iduplicable_get_type( void )
84 {
85 static GType iface_type = 0;
86
87 if( !iface_type ){
88 iface_type = register_type();
89 }
90
91 return( iface_type );
92 }
93
94 static GType
register_type(void)95 register_type( void )
96 {
97 static const gchar *thisfn = "na_iduplicable_register_type";
98 GType type;
99
100 static const GTypeInfo info = {
101 sizeof( NAIDuplicableInterface ),
102 ( GBaseInitFunc ) interface_base_init,
103 ( GBaseFinalizeFunc ) interface_base_finalize,
104 NULL,
105 NULL,
106 NULL,
107 0,
108 0,
109 NULL
110 };
111
112 g_debug( "%s", thisfn );
113
114 type = g_type_register_static( G_TYPE_INTERFACE, "NAIDuplicable", &info, 0 );
115
116 g_type_interface_add_prerequisite( type, G_TYPE_OBJECT );
117
118 return( type );
119 }
120
121 static void
interface_base_init(NAIDuplicableInterface * klass)122 interface_base_init( NAIDuplicableInterface *klass )
123 {
124 static const gchar *thisfn = "na_iduplicable_interface_base_init";
125
126 if( !st_initializations ){
127
128 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
129
130 klass->private = g_new0( NAIDuplicableInterfacePrivate, 1 );
131
132 klass->private->consumers = NULL;
133
134 klass->copy = NULL;
135 klass->are_equal = NULL;
136 klass->is_valid = NULL;
137
138 /**
139 * NAIDuplicable::modified-changed:
140 *
141 * This signal is emitted by #NAIDuplicable when the modification
142 * status of an object has been modified.
143 *
144 * The default class handler propagates the signal to registered
145 * consumers.
146 *
147 * Signal args: New modification status
148 *
149 * Handler prototype:
150 * void ( *handler )( NAIDuplicable *duplicable, NAObject *object, gboolean is_modified, gpointer user_data );
151 *
152 * When the signal is first emitted, thus on NAIDuplicable, @duplicable
153 * and @object are pointers to the same address. This duplication is
154 * relevant when propagating the signal to customer, as the signal is
155 * emitted on the customer itself, while we still need the @object
156 * pointer.
157 */
158 st_signals[ MODIFIED_CHANGED ] = g_signal_new_class_handler(
159 IDUPLICABLE_SIGNAL_MODIFIED_CHANGED,
160 G_TYPE_OBJECT,
161 G_SIGNAL_RUN_CLEANUP,
162 G_CALLBACK( on_modified_changed_class_handler ),
163 NULL,
164 NULL,
165 na_cclosure_marshal_VOID__POINTER_BOOLEAN,
166 G_TYPE_NONE,
167 2,
168 G_TYPE_POINTER, G_TYPE_BOOLEAN );
169
170 /**
171 * NAIDuplicable::valid-changed:
172 *
173 * This signal is emitted by #NAIDuplicable when the validity
174 * status of an object has been modified.
175 *
176 * The default class handler propagates the signal to registered
177 * consumers.
178 *
179 * Signal args: New validity status
180 *
181 * Handler prototype:
182 * void ( *handler )( NAIDuplicable *duplicable, NAObject *object, gboolean is_valid, gpointer user_data );
183 *
184 * When the signal is first emitted, thus on NAIDuplicable, @duplicable
185 * and @object are pointers to the same address. This duplication is
186 * relevant when propagating the signal to customer, as the signal is
187 * emitted on the customer itself, while we still need the @object
188 * pointer.
189 */
190 st_signals[ VALID_CHANGED ] = g_signal_new_class_handler(
191 IDUPLICABLE_SIGNAL_VALID_CHANGED,
192 G_TYPE_OBJECT,
193 G_SIGNAL_RUN_CLEANUP,
194 G_CALLBACK( on_valid_changed_class_handler ),
195 NULL,
196 NULL,
197 na_cclosure_marshal_VOID__POINTER_BOOLEAN,
198 G_TYPE_NONE,
199 2,
200 G_TYPE_POINTER, G_TYPE_BOOLEAN );
201
202 st_interface = klass;
203 }
204
205 st_initializations += 1;
206 }
207
208 static void
interface_base_finalize(NAIDuplicableInterface * klass)209 interface_base_finalize( NAIDuplicableInterface *klass )
210 {
211 static const gchar *thisfn = "na_iduplicable_interface_base_finalize";
212
213 st_initializations -= 1;
214
215 if( !st_initializations ){
216
217 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
218
219 release_signal_consumers( klass->private->consumers );
220
221 g_free( klass->private );
222 }
223 }
224
225 /**
226 * na_iduplicable_dispose:
227 * @object: the #NAIDuplicable object to be initialized.
228 *
229 * Releases resources.
230 *
231 * Since: 2.30
232 */
233 void
na_iduplicable_dispose(const NAIDuplicable * object)234 na_iduplicable_dispose( const NAIDuplicable *object )
235 {
236 DuplicableStr *str;
237
238 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
239
240 str = get_duplicable_str( object );
241 g_free( str );
242 g_object_set_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE, NULL );
243 }
244
245 /**
246 * na_iduplicable_dump:
247 * @object: the #NAIDuplicable object to be dumped.
248 *
249 * Dumps via g_debug the properties of the object.
250 *
251 * We ouput here only the data we set ourselves againt the
252 * #NAIDuplicable -implemented object.
253 *
254 * This function should be called by the implementation when it dumps
255 * itself its own content.
256 *
257 * Since: 2.30
258 */
259 void
na_iduplicable_dump(const NAIDuplicable * object)260 na_iduplicable_dump( const NAIDuplicable *object )
261 {
262 static const gchar *thisfn = "na_iduplicable_dump";
263 DuplicableStr *str;
264
265 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
266
267 str = get_duplicable_str( object );
268
269 g_debug( "| %s: origin=%p", thisfn, ( void * ) str->origin );
270 g_debug( "| %s: modified=%s", thisfn, str->modified ? "True" : "False" );
271 g_debug( "| %s: valid=%s", thisfn, str->valid ? "True" : "False" );
272 }
273
274 /**
275 * na_iduplicable_duplicate:
276 * @object: the #NAIDuplicable object to be duplicated.
277 * @mode: the %DuplicableMode duplication mode.
278 *
279 * Exactly duplicates a #NAIDuplicable -implemented object, including
280 * modification and validity status which are copied from @object to
281 * the duplicated one.
282 *
283 * Returns: a new #NAIDuplicable.
284 *
285 * Since: 2.30
286 */
287 NAIDuplicable *
na_iduplicable_duplicate(const NAIDuplicable * object,guint mode)288 na_iduplicable_duplicate( const NAIDuplicable *object, guint mode )
289 {
290 static const gchar *thisfn = "na_iduplicable_duplicate";
291 NAIDuplicable *dup;
292 DuplicableStr *dup_str, *obj_str;
293
294 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
295
296 g_debug( "%s: object=%p (%s)",
297 thisfn,
298 ( void * ) object, G_OBJECT_TYPE_NAME( object ));
299
300 dup = g_object_new( G_OBJECT_TYPE( object ), NULL );
301
302 v_copy( dup, object, mode );
303
304 dup_str = get_duplicable_str( dup );
305 obj_str = get_duplicable_str( object );
306
307 dup_str->origin = ( NAIDuplicable * ) object;
308 dup_str->modified = obj_str->modified;
309 dup_str->valid = obj_str->valid;
310
311 return( dup );
312 }
313
314 /**
315 * na_iduplicable_check_status:
316 * @object: the #NAIDuplicable object to be checked.
317 *
318 * Checks the edition status of the #NAIDuplicable object, and set up
319 * the corresponding properties.
320 *
321 * This function is supposed to be called each time the object may have
322 * been modified in order to set the corresponding properties. Helper
323 * functions na_iduplicable_is_modified() and na_iduplicable_is_valid()
324 * will then only return the current value of the properties.
325 *
326 * na_iduplicable_check_status() is not, as itself, recursive.
327 * That is, the modification and validity status are only set on the
328 * specified object.
329 * #NAObject implementation has chosen to handle itself the recursivity:
330 * na_object_check_status() so first check status for children, before
331 * calling this function.
332 *
333 * Since: 2.30
334 */
335 void
na_iduplicable_check_status(const NAIDuplicable * object)336 na_iduplicable_check_status( const NAIDuplicable *object )
337 {
338 static const gchar *thisfn = "na_iduplicable_check_status";
339 DuplicableStr *str;
340 gboolean was_modified, was_valid;
341
342 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
343
344 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
345
346 str = get_duplicable_str( object );
347
348 was_modified = str->modified;
349 was_valid = str->valid;
350
351 if( str->origin ){
352 g_debug( "%s: vs. origin=%p (%s)", thisfn, ( void * ) str->origin, G_OBJECT_TYPE_NAME( str->origin ));
353 g_return_if_fail( NA_IS_IDUPLICABLE( str->origin ));
354 str->modified = !v_are_equal( str->origin, object );
355
356 } else {
357 str->modified = TRUE;
358 }
359
360 if( was_modified != str->modified ){
361 g_debug( "%s: %p (%s) status changed to modified=%s",
362 thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ), str->modified ? "True":"False" );
363 g_signal_emit_by_name( G_OBJECT( object ), IDUPLICABLE_SIGNAL_MODIFIED_CHANGED, object, str->modified );
364 }
365
366 str->valid = v_is_valid( object );
367
368 if( was_valid != str->valid ){
369 g_debug( "%s: %p (%s) status changed to valid=%s",
370 thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ), str->valid ? "True":"False" );
371 g_signal_emit_by_name( G_OBJECT( object ), IDUPLICABLE_SIGNAL_VALID_CHANGED, object, str->valid );
372 }
373 }
374
375 /**
376 * na_iduplicable_get_origin:
377 * @object: the #NAIDuplicable object whose origin is to be returned.
378 *
379 * Returns the origin of a duplicated #NAIDuplicable.
380 *
381 * Returns: the original #NAIDuplicable, or NULL.
382 *
383 * Since: 2.30
384 */
385 NAIDuplicable *
na_iduplicable_get_origin(const NAIDuplicable * object)386 na_iduplicable_get_origin( const NAIDuplicable *object )
387 {
388 NAIDuplicable *origin;
389 DuplicableStr *str;
390
391 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), NULL );
392
393 str = get_duplicable_str( object );
394 origin = str->origin;
395
396 return( origin );
397 }
398
399 /**
400 * na_iduplicable_is_valid:
401 * @object: the #NAIDuplicable object whose status is to be returned.
402 *
403 * Returns the current value of the relevant property
404 * without rechecking the edition status itself.
405 *
406 * Returns: %TRUE is the provided object is valid.
407 *
408 * Since: 2.30
409 */
410 gboolean
na_iduplicable_is_valid(const NAIDuplicable * object)411 na_iduplicable_is_valid( const NAIDuplicable *object )
412 {
413 gboolean is_valid;
414 DuplicableStr *str;
415
416 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), FALSE );
417
418 str = get_duplicable_str( object );
419 is_valid = str->valid;
420
421 return( is_valid );
422 }
423
424 /**
425 * na_iduplicable_is_modified:
426 * @object: the #NAIDuplicable object whose status is to be returned.
427 *
428 * Returns the current value of the 'is_modified'
429 * property without rechecking the edition status itself.
430 *
431 * Returns: %TRUE is the provided object has been modified regarding of
432 * the original one.
433 *
434 * Since: 2.30
435 */
436 gboolean
na_iduplicable_is_modified(const NAIDuplicable * object)437 na_iduplicable_is_modified( const NAIDuplicable *object )
438 {
439 gboolean is_modified;
440 DuplicableStr *str;
441
442 g_return_val_if_fail( NA_IS_IDUPLICABLE( object ), FALSE );
443
444 str = get_duplicable_str( object );
445 is_modified = str->modified;
446
447 return( is_modified );
448 }
449
450 /**
451 * na_iduplicable_set_origin:
452 * @object: the #NAIDuplicable object whose origin is to be set.
453 * @origin: the new original #NAIDuplicable.
454 *
455 * Sets the new origin of a duplicated #NAIDuplicable.
456 *
457 * Since: 2.30
458 */
459 void
na_iduplicable_set_origin(NAIDuplicable * object,const NAIDuplicable * origin)460 na_iduplicable_set_origin( NAIDuplicable *object, const NAIDuplicable *origin )
461 {
462 DuplicableStr *str;
463
464 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
465 g_return_if_fail( NA_IS_IDUPLICABLE( origin ) || !origin );
466
467 str = get_duplicable_str( object );
468 str->origin = ( NAIDuplicable * ) origin;
469 }
470
471 #ifdef NA_ENABLE_DEPRECATED
472 /**
473 * na_iduplicable_set_modified:
474 * @object: the #NAIDuplicable object whose modification status is to be set.
475 * @modified: the new modification status #NAIDuplicable.
476 *
477 * Sets the new modification status of a duplicated #NAIDuplicable.
478 *
479 * Since: 2.30
480 * Deprecated: 3.1
481 */
482 void
na_iduplicable_set_modified(NAIDuplicable * object,gboolean modified)483 na_iduplicable_set_modified( NAIDuplicable *object, gboolean modified )
484 {
485 DuplicableStr *str;
486
487 g_return_if_fail( NA_IS_IDUPLICABLE( object ));
488
489 str = get_duplicable_str( object );
490 str->modified = modified;
491 }
492 #endif /* NA_ENABLE_DEPRECATED */
493
494 static void
v_copy(NAIDuplicable * target,const NAIDuplicable * source,guint mode)495 v_copy( NAIDuplicable *target, const NAIDuplicable *source, guint mode )
496 {
497 if( NA_IDUPLICABLE_GET_INTERFACE( target )->copy ){
498 NA_IDUPLICABLE_GET_INTERFACE( target )->copy( target, source, mode );
499 }
500 }
501
502 static gboolean
v_are_equal(const NAIDuplicable * a,const NAIDuplicable * b)503 v_are_equal( const NAIDuplicable *a, const NAIDuplicable *b )
504 {
505 if( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal ){
506 return( NA_IDUPLICABLE_GET_INTERFACE( a )->are_equal( a, b ));
507 }
508
509 return( TRUE );
510 }
511
512 static gboolean
v_is_valid(const NAIDuplicable * object)513 v_is_valid( const NAIDuplicable *object )
514 {
515 if( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid ){
516 return( NA_IDUPLICABLE_GET_INTERFACE( object )->is_valid( object ));
517 }
518
519 return( TRUE );
520 }
521
522 /**
523 * na_iduplicable_register_consumer:
524 * @consumer: the target instance.
525 *
526 * This function registers a consumer, i.e. an instance to which edition
527 * status signals will be propagated.
528 *
529 * Since: 2.30
530 */
531 void
na_iduplicable_register_consumer(GObject * consumer)532 na_iduplicable_register_consumer( GObject *consumer )
533 {
534 g_return_if_fail( st_interface );
535
536 g_debug( "na_iduplicable_register_consumer: consumer=%p", ( void * ) consumer );
537
538 st_interface->private->consumers = g_list_prepend( st_interface->private->consumers, consumer );
539 }
540
541 static void
on_modified_changed_class_handler(NAIDuplicable * instance,GObject * object,gboolean is_modified)542 on_modified_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_modified )
543 {
544 if( NA_IS_IDUPLICABLE( instance )){
545 propagate_signal_to_consumers( instance, IDUPLICABLE_SIGNAL_MODIFIED_CHANGED, object, is_modified );
546 }
547 }
548
549 static void
on_valid_changed_class_handler(NAIDuplicable * instance,GObject * object,gboolean is_valid)550 on_valid_changed_class_handler( NAIDuplicable *instance, GObject *object, gboolean is_valid )
551 {
552 if( NA_IS_IDUPLICABLE( instance )){
553 propagate_signal_to_consumers( instance, IDUPLICABLE_SIGNAL_VALID_CHANGED, object, is_valid );
554 }
555 }
556
557 static void
propagate_signal_to_consumers(NAIDuplicable * instance,const gchar * signal,GObject * object,gboolean new_status)558 propagate_signal_to_consumers( NAIDuplicable *instance, const gchar *signal, GObject *object, gboolean new_status )
559 {
560 static const gchar *thisfn = "na_iduplicable_propagate_signals_to_consumers";
561 GList *ic;
562
563 g_return_if_fail( NA_IS_IDUPLICABLE( instance ));
564
565 g_debug( "%s: instance=%p, signal=%s", thisfn, ( void * ) instance, signal );
566
567 for( ic = st_interface->private->consumers ; ic ; ic = ic->next ){
568 g_signal_emit_by_name( ic->data, signal, object, new_status );
569 }
570 }
571
572 static void
release_signal_consumers(GList * consumers)573 release_signal_consumers( GList *consumers )
574 {
575 g_list_free( consumers );
576 }
577
578 static DuplicableStr *
get_duplicable_str(const NAIDuplicable * object)579 get_duplicable_str( const NAIDuplicable *object )
580 {
581 DuplicableStr *str;
582
583 str = ( DuplicableStr * ) g_object_get_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE );
584
585 if( !str ){
586 str = g_new0( DuplicableStr, 1 );
587
588 str->origin = NULL;
589 str->modified = FALSE;
590 str->valid = TRUE;
591
592 g_object_set_data( G_OBJECT( object ), NA_IDUPLICABLE_DATA_DUPLICABLE, str );
593 }
594
595 return( str );
596 }
597