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 <glib/gi18n.h>
35 
36 #include <api/na-core-utils.h>
37 #include <api/na-object-api.h>
38 
39 /* private class data
40  */
41 struct _NAObjectIdClassPrivate {
42 	void *empty;						/* so that gcc -pedantic is happy */
43 };
44 
45 /* private instance data
46  */
47 struct _NAObjectIdPrivate {
48 	gboolean   dispose_has_run;
49 };
50 
51 static NAObjectClass *st_parent_class = NULL;
52 
53 static GType    register_type( void );
54 static void     class_init( NAObjectIdClass *klass );
55 static void     instance_init( GTypeInstance *instance, gpointer klass );
56 static void     instance_dispose( GObject *object );
57 static void     instance_finalize( GObject *object );
58 
59 static gchar   *v_new_id( const NAObjectId *object, const NAObjectId *new_parent );
60 
61 GType
na_object_id_get_type(void)62 na_object_id_get_type( void )
63 {
64 	static GType item_type = 0;
65 
66 	if( item_type == 0 ){
67 		item_type = register_type();
68 	}
69 
70 	return( item_type );
71 }
72 
73 static GType
register_type(void)74 register_type( void )
75 {
76 	static const gchar *thisfn = "na_object_id_register_type";
77 	GType type;
78 
79 	static GTypeInfo info = {
80 		sizeof( NAObjectIdClass ),
81 		NULL,
82 		NULL,
83 		( GClassInitFunc ) class_init,
84 		NULL,
85 		NULL,
86 		sizeof( NAObjectId ),
87 		0,
88 		( GInstanceInitFunc ) instance_init
89 	};
90 
91 	g_debug( "%s", thisfn );
92 
93 	type = g_type_register_static( NA_TYPE_OBJECT, "NAObjectId", &info, 0 );
94 
95 	return( type );
96 }
97 
98 static void
class_init(NAObjectIdClass * klass)99 class_init( NAObjectIdClass *klass )
100 {
101 	static const gchar *thisfn = "na_object_id_class_init";
102 	GObjectClass *object_class;
103 
104 	g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
105 
106 	st_parent_class = g_type_class_peek_parent( klass );
107 
108 	object_class = G_OBJECT_CLASS( klass );
109 	object_class->dispose = instance_dispose;
110 	object_class->finalize = instance_finalize;
111 
112 	klass->private = g_new0( NAObjectIdClassPrivate, 1 );
113 }
114 
115 static void
instance_init(GTypeInstance * instance,gpointer klass)116 instance_init( GTypeInstance *instance, gpointer klass )
117 {
118 	NAObjectId *self;
119 
120 	g_return_if_fail( NA_IS_OBJECT_ID( instance ));
121 
122 	self = NA_OBJECT_ID( instance );
123 
124 	self->private = g_new0( NAObjectIdPrivate, 1 );
125 }
126 
127 /*
128  * note that when the tree store is cleared, Gtk begins with the deepest
129  * levels, so that children are disposed before their parent
130  * as we try to dispose all children when disposing a parent, we have to
131  * remove a disposing child from its parent
132  */
133 static void
instance_dispose(GObject * object)134 instance_dispose( GObject *object )
135 {
136 	NAObjectId *self;
137 	NAObjectItem *parent;
138 
139 	g_return_if_fail( NA_IS_OBJECT_ID( object ));
140 
141 	self = NA_OBJECT_ID( object );
142 
143 	if( !self->private->dispose_has_run ){
144 
145 		self->private->dispose_has_run = TRUE;
146 
147 		parent = na_object_get_parent( object );
148 		if( parent ){
149 			na_object_remove_item( parent, object );
150 		}
151 
152 		self->private->dispose_has_run = TRUE;
153 
154 		/* chain up to the parent class */
155 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
156 			G_OBJECT_CLASS( st_parent_class )->dispose( object );
157 		}
158 	}
159 }
160 
161 static void
instance_finalize(GObject * object)162 instance_finalize( GObject *object )
163 {
164 	NAObjectId *self;
165 
166 	g_return_if_fail( NA_IS_OBJECT_ID( object ));
167 
168 	self = NA_OBJECT_ID( object );
169 
170 	g_free( self->private );
171 
172 	/* chain call to parent class */
173 	if( G_OBJECT_CLASS( st_parent_class )->finalize ){
174 		G_OBJECT_CLASS( st_parent_class )->finalize( object );
175 	}
176 }
177 
178 /**
179  * na_object_id_sort_alpha_asc:
180  * @a: first #NAObjectId.
181  * @b: second #NAObjectId.
182  *
183  * Sort the objects in alphabetical ascending order of their label.
184  *
185  * Returns:
186  *
187  * <itemizedlist>
188  *   <listitem>
189  *     <para>-1 if @a must be sorted before @b,</para>
190  *   </listitem>
191  *   <listitem>
192  *     <para>0 if @a and @b are equal from the local point of view,</para>
193  *   </listitem>
194  *   <listitem>
195  *     <para>1 if @a must be sorted after @b.</para>
196  *   </listitem>
197  * </itemizedlist>
198  *
199  * Since: 2.30
200  */
201 gint
na_object_id_sort_alpha_asc(const NAObjectId * a,const NAObjectId * b)202 na_object_id_sort_alpha_asc( const NAObjectId *a, const NAObjectId *b )
203 {
204 	gchar *label_a, *label_b;
205 	gint compare;
206 
207 	label_a = na_object_get_label( a );
208 	label_b = na_object_get_label( b );
209 
210 	compare = na_core_utils_str_collate( label_a, label_b );
211 
212 	g_free( label_b );
213 	g_free( label_a );
214 
215 	return( compare );
216 }
217 
218 /**
219  * na_object_id_sort_alpha_desc:
220  * @a: first #NAObjectId.
221  * @b: second #NAObjectId.
222  *
223  * Sort the objects in alphabetical descending order of their label.
224  *
225  * Returns:
226  *
227  * <itemizedlist>
228  *   <listitem>
229  *     <para>-1 if @a must be sorted before @b,</para>
230  *   </listitem>
231  *   <listitem>
232  *     <para>0 if @a and @b are equal from the local point of view,</para>
233  *   </listitem>
234  *   <listitem>
235  *     <para>1 if @a must be sorted after @b.</para>
236  *   </listitem>
237  * </itemizedlist>
238  *
239  * Since: 2.30
240  */
241 gint
na_object_id_sort_alpha_desc(const NAObjectId * a,const NAObjectId * b)242 na_object_id_sort_alpha_desc( const NAObjectId *a, const NAObjectId *b )
243 {
244 	return( -1 * na_object_id_sort_alpha_asc( a, b ));
245 }
246 
247 /**
248  * na_object_id_prepare_for_paste:
249  * @object: the #NAObjectId object to be pasted.
250  * @relabel: whether this object should be relabeled when pasted.
251  * @renumber: whether this item should be renumbered ?
252  * @parent: the parent of @object, or %NULL.
253  *
254  * Prepares @object to be pasted.
255  *
256  * If a #NAObjectProfile, then @object is attached to the specified
257  * #NAObjectAction @action. The identifier is always renumbered to be
258  * suitable with the already existing profiles.
259  *
260  * If a #NAObjectAction or a #NAObjectMenu, a new identifier is allocated
261  * if and only if @relabel is %TRUE.
262  *
263  * Actual relabeling takes place if @relabel is %TRUE, depending of the
264  * user preferences.
265  *
266  * Since: 2.30
267  */
268 void
na_object_id_prepare_for_paste(NAObjectId * object,gboolean relabel,gboolean renumber,NAObjectId * parent)269 na_object_id_prepare_for_paste( NAObjectId *object, gboolean relabel, gboolean renumber, NAObjectId *parent )
270 {
271 	static const gchar *thisfn = "na_object_id_prepare_for_paste";
272 	GList *subitems, *it;
273 
274 	g_return_if_fail( NA_IS_OBJECT_ID( object ));
275 	g_return_if_fail( !parent || NA_IS_OBJECT_ITEM( parent ));
276 
277 	if( !object->private->dispose_has_run ){
278 
279 		g_debug( "%s: object=%p, relabel=%s, renumber=%s, parent=%p",
280 				thisfn, ( void * ) object, relabel ? "True":"False", renumber ? "True":"False", ( void * ) parent );
281 
282 		if( NA_IS_OBJECT_PROFILE( object )){
283 			na_object_set_parent( object, parent );
284 			na_object_set_new_id( object, parent );
285 			if( renumber && relabel ){
286 				na_object_set_copy_of_label( object );
287 			}
288 
289 		} else {
290 			if( renumber ){
291 				na_object_set_new_id( object, NULL );
292 				if( relabel ){
293 					na_object_set_copy_of_label( object );
294 				}
295 				na_object_set_provider( object, NULL );
296 				na_object_set_provider_data( object, NULL );
297 				na_object_set_readonly( object, FALSE );
298 			}
299 			if( NA_IS_OBJECT_MENU( object )){
300 				subitems = na_object_get_items( object );
301 				for( it = subitems ; it ; it = it->next ){
302 					na_object_prepare_for_paste( it->data, relabel, renumber, NULL );
303 				}
304 			}
305 		}
306 	}
307 }
308 
309 /**
310  * na_object_id_set_copy_of_label:
311  * @object: the #NAObjectId object whose label is to be changed.
312  *
313  * Sets the 'Copy of' label.
314  *
315  * Since: 2.30
316  */
317 void
na_object_id_set_copy_of_label(NAObjectId * object)318 na_object_id_set_copy_of_label( NAObjectId *object )
319 {
320 	gchar *label, *new_label;
321 
322 	g_return_if_fail( NA_IS_OBJECT_ID( object ));
323 
324 	if( !object->private->dispose_has_run ){
325 
326 		label = na_object_get_label( object );
327 
328 		/* i18n: copied items have a label as 'Copy of original label' */
329 		new_label = g_strdup_printf( _( "Copy of %s" ), label );
330 
331 		na_object_set_label( object, new_label );
332 
333 		g_free( new_label );
334 		g_free( label );
335 	}
336 }
337 
338 /**
339  * na_object_id_set_new_id:
340  * @object: the #NAObjectId object whose internal identifier is to be
341  * set.
342  * @new_parent: if @object is a #NAObjectProfile, then @new_parent
343  * should be set to the #NAObjectAction new parent. Else, it would not
344  * be possible to allocate a new profile id compatible with already
345  * existing ones.
346  *
347  * Request a new id to the derived class, and set it.
348  *
349  * Since: 2.30
350  */
351 void
na_object_id_set_new_id(NAObjectId * object,const NAObjectId * new_parent)352 na_object_id_set_new_id( NAObjectId *object, const NAObjectId *new_parent )
353 {
354 	static const gchar *thisfn = "na_object_id_set_new_id";
355 	gchar *id;
356 
357 	g_return_if_fail( NA_IS_OBJECT_ID( object ));
358 	g_return_if_fail( !new_parent || NA_IS_OBJECT_ITEM( new_parent ));
359 
360 	if( !object->private->dispose_has_run ){
361 
362 		g_debug( "%s: object=%p (%s), new_parent=%p (%s)",
363 				thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ),
364 				( void * ) new_parent, new_parent ? G_OBJECT_TYPE_NAME( new_parent ) : "n/a" );
365 
366 		id = v_new_id( object, new_parent );
367 
368 		if( id ){
369 			na_object_set_id( object, id );
370 			g_free( id );
371 		}
372 	}
373 }
374 
375 static gchar *
v_new_id(const NAObjectId * object,const NAObjectId * new_parent)376 v_new_id( const NAObjectId *object, const NAObjectId *new_parent )
377 {
378 	gchar *new_id = NULL;
379 
380 	if( NA_OBJECT_ID_GET_CLASS( object )->new_id ){
381 		new_id = NA_OBJECT_ID_GET_CLASS( object )->new_id( object, new_parent );
382 	}
383 
384 	return( new_id );
385 }
386