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