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 #include <libintl.h>
36 #include <string.h>
37 
38 #include <api/na-core-utils.h>
39 #include <api/na-object-api.h>
40 
41 #include <core/na-gnome-vfs-uri.h>
42 #include <core/na-importer.h>
43 
44 #include "nact-application.h"
45 #include "nact-clipboard.h"
46 #include "nact-main-statusbar.h"
47 #include "nact-main-window.h"
48 #include "nact-tree-model.h"
49 #include "nact-tree-model-priv.h"
50 #include "nact-tree-ieditable.h"
51 
52 /*
53  * call once egg_tree_multi_drag_add_drag_support( treeview ) at init time (before gtk_main)
54  *
55  * when we start with drag
56  * 	 call once egg_tree_multi_dnd_on_button_press_event( treeview, event, drag_source )
57  *   call many egg_tree_multi_dnd_on_motion_event( treeview, event, drag_source )
58  *     until mouse quits the selected area
59  *
60  * as soon as mouse has quitted the selected area
61  *   call once egg_tree_multi_dnd_stop_drag_check( treeview )
62  *   call once nact_tree_model_dnd_imulti_drag_source_row_draggable: drag_source=0x92a0d70, path_list=0x9373c90
63  *   call once nact_tree_model_dnd_on_drag_begin
64  *     nact_clipboard_dnd_clear()
65  *   call once nact_clipboard_on_drag_begin( treeview, context, main_window )
66  *
67  * when we drop (e.g. in Nautilus)
68  *   call once egg_tree_multi_dnd_on_drag_data_get( treeview, context, selection_data, info=0, time )
69  *   call once nact_tree_model_dnd_imulti_drag_source_drag_data_get( drag_source, context, selection_data, path_list, atom=XdndDirectSave0 )
70  *     nact_clipboard_dnd_set()
71  *   call once nact_tree_model_dnd_on_drag_end
72  *     nact_clipboard_dnd_drag_end
73  *       nact_clipboard_get_from_dnd_clipboard_callback
74  *     nact_clipboard_dnd_clear
75  *
76  * when we drop in Nautilus-Actions
77  *   call once egg_tree_multi_dnd_on_drag_data_get( treeview, context, selection_data, info=0, time )
78  *   call once nact_tree_model_imulti_drag_source_drag_data_get( drag_source, context, selection_data, path_list, atom=XdndNautilusActions )
79  *   call once nact_clipboard_get_data_for_intern_use
80  *   call once nact_tree_model_idrag_dest_drag_data_received
81  *   call once nact_clipboard_on_drag_end( treeview, context, main_window )
82  */
83 
84 #define MAX_XDS_ATOM_VAL_LEN			4096
85 #define TEXT_ATOM						gdk_atom_intern( "text/plain", FALSE )
86 #define XDS_ATOM						gdk_atom_intern( "XdndDirectSave0", FALSE )
87 #define XDS_FILENAME					"xds.txt"
88 
89 #define NACT_ATOM						gdk_atom_intern( "XdndNautilusActions", FALSE )
90 
91 /* as a dnd source, we provide
92  * - a special XdndNautilusAction format for internal move/copy
93  * - a XdndDirectSave, suitable for exporting to a file manager
94  *   (note that Nautilus recognized the "XdndDirectSave0" format as XDS
95  *   protocol)
96  * - a text (xml) format, to go to clipboard or a text editor
97  */
98 static GtkTargetEntry dnd_source_formats[] = {
99 	{ "XdndNautilusActions", GTK_TARGET_SAME_WIDGET, NACT_XCHANGE_FORMAT_NACT },
100 	{ "XdndDirectSave0",     GTK_TARGET_OTHER_APP,   NACT_XCHANGE_FORMAT_XDS },
101 	{ "application/xml",     GTK_TARGET_OTHER_APP,   NACT_XCHANGE_FORMAT_APPLICATION_XML },
102 	{ "text/plain",          GTK_TARGET_OTHER_APP,   NACT_XCHANGE_FORMAT_TEXT_PLAIN },
103 };
104 
105 /* as a dnd dest, we accept
106  * - of course, the same special XdndNautilusAction format for internal move/copy
107  * - a list of uris, to be imported
108  * - a XML buffer, to be imported
109  * - a plain text, which we are going to try to import as a XML description
110  */
111 GtkTargetEntry tree_model_dnd_dest_formats[] = {
112 	{ "XdndNautilusActions", 0, NACT_XCHANGE_FORMAT_NACT },
113 	{ "text/uri-list",       0, NACT_XCHANGE_FORMAT_URI_LIST },
114 	{ "application/xml",     0, NACT_XCHANGE_FORMAT_APPLICATION_XML },
115 	{ "text/plain",          0, NACT_XCHANGE_FORMAT_TEXT_PLAIN },
116 };
117 
118 guint tree_model_dnd_dest_formats_count = G_N_ELEMENTS( tree_model_dnd_dest_formats );
119 
120 static const gchar *st_refuse_drop_profile = N_( "Unable to drop a profile here" );
121 static const gchar *st_refuse_drop_item = N_( "Unable to drop an action or a menu here" );
122 static const gchar *st_parent_not_writable = N_( "Unable to drop here as parent is not writable" );
123 static const gchar *st_level_zero_not_writable = N_( "Unable to drop here as level zero is not writable" );
124 
125 static gboolean      drop_inside( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selection_data );
126 static gboolean      is_drop_possible( NactTreeModel *model, GtkTreePath *dest, NAObjectItem **parent );
127 static gboolean      is_drop_possible_before_iter( NactTreeModel *model, GtkTreeIter *iter, NactMainWindow *window, NAObjectItem **parent );
128 static gboolean      is_drop_possible_into_dest( NactTreeModel *model, GtkTreePath *dest, NactMainWindow *window, NAObjectItem **parent );
129 static void          drop_inside_move_dest( NactTreeModel *model, GList *rows, GtkTreePath **dest );
130 static gboolean      drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selection_data );
131 static NAObjectItem *is_dropped_already_exists( const NAObjectItem *importing, const NactMainWindow *window );
132 static char         *get_xds_atom_value( GdkDragContext *context );
133 static gboolean      is_parent_accept_new_children( NactApplication *application, NactMainWindow *window, NAObjectItem *parent );
134 static guint         target_atom_to_id( GdkAtom atom );
135 
136 /**
137  * nact_tree_model_dnd_idrag_dest_drag_data_received:
138  * @drag_dest:
139  * @dest:
140  * @selection_data:
141  *
142  * Called when a drop occurs in the treeview;
143  * this may be an import action from the outside world, or a move/copy
144  * inside of the tree.
145  *
146  * Returns: %TRUE if the specified rows were successfully inserted at
147  * the given dest, %FALSE else.
148  *
149  * When importing:
150  * - selection=XdndSelection
151  * - target=text/uri-list
152  * - type=text/uri-list
153  *
154  * When moving/copy from the treeview to the treeview:
155  * - selection=XdndSelection
156  * - target=XdndNautilusActions
157  * - type=XdndNautilusActions
158  */
159 gboolean
nact_tree_model_dnd_idrag_dest_drag_data_received(GtkTreeDragDest * drag_dest,GtkTreePath * dest,GtkSelectionData * selection_data)160 nact_tree_model_dnd_idrag_dest_drag_data_received( GtkTreeDragDest *drag_dest, GtkTreePath *dest, GtkSelectionData  *selection_data )
161 {
162 	static const gchar *thisfn = "nact_tree_model_dnd_idrag_dest_drag_data_received";
163 	gboolean result = FALSE;
164 	NactTreeModel *model;
165 	gchar *atom_name;
166 	guint info;
167 	gchar *path_str;
168 	GdkAtom selection_data_selection;
169 	GdkAtom selection_data_target;
170 	GdkAtom selection_data_type;
171 	gint selection_data_format;
172 	gint selection_data_length;
173 
174 	g_return_val_if_fail( NACT_IS_TREE_MODEL( drag_dest ), result );
175 
176 	model = NACT_TREE_MODEL( drag_dest );
177 
178 	if( !model->private->dispose_has_run ){
179 
180 		g_debug( "%s: drag_dest=%p (ref_count=%d), dest=%p, selection_data=%p",
181 				thisfn,
182 				( void * ) drag_dest, G_OBJECT( drag_dest )->ref_count,
183 				( void * ) dest,
184 				( void * ) selection_data );
185 
186 		selection_data_selection = gtk_selection_data_get_selection( selection_data );
187 		atom_name = gdk_atom_name( selection_data_selection );
188 		g_debug( "%s: selection=%s", thisfn, atom_name );
189 		g_free( atom_name );
190 
191 		selection_data_target = gtk_selection_data_get_target( selection_data );
192 		atom_name = gdk_atom_name( selection_data_target );
193 		g_debug( "%s: target=%s", thisfn, atom_name );
194 		g_free( atom_name );
195 
196 		selection_data_type = gtk_selection_data_get_data_type( selection_data );
197 		atom_name = gdk_atom_name( selection_data_type );
198 		g_debug( "%s: type=%s", thisfn, atom_name );
199 		g_free( atom_name );
200 
201 		selection_data_format = gtk_selection_data_get_format( selection_data );
202 		selection_data_length = gtk_selection_data_get_length( selection_data );
203 		g_debug( "%s: format=%d, length=%d", thisfn, selection_data_format, selection_data_length );
204 
205 		info = target_atom_to_id( selection_data_type );
206 		g_debug( "%s: info=%u", thisfn, info );
207 
208 		path_str = gtk_tree_path_to_string( dest );
209 		g_debug( "%s: dest_path=%s", thisfn, path_str );
210 		g_free( path_str );
211 
212 		switch( info ){
213 			case NACT_XCHANGE_FORMAT_NACT:
214 				result = drop_inside( model, dest, selection_data );
215 				break;
216 
217 			/* drop some actions from outside
218 			 * most probably from the file manager as a list of uris
219 			 */
220 			case NACT_XCHANGE_FORMAT_URI_LIST:
221 				result = drop_uri_list( model, dest, selection_data );
222 				break;
223 
224 			default:
225 				break;
226 		}
227 	}
228 
229 	return( result );
230 }
231 
232 /**
233  * nact_tree_model_dnd_idrag_dest_row_drop_possible:
234  * @drag_dest:
235  * @dest_path:
236  * @selection_data:
237  *
238  * Seems to only be called when the drop in _on_ a row (a square is
239  * displayed), but not when dropped between two rows (a line is displayed),
240  * nor during the motion.
241  */
242 gboolean
nact_tree_model_dnd_idrag_dest_row_drop_possible(GtkTreeDragDest * drag_dest,GtkTreePath * dest_path,GtkSelectionData * selection_data)243 nact_tree_model_dnd_idrag_dest_row_drop_possible( GtkTreeDragDest *drag_dest, GtkTreePath *dest_path, GtkSelectionData *selection_data )
244 {
245 	static const gchar *thisfn = "nact_tree_model_dnd_idrag_dest_row_drop_possible";
246 
247 	g_debug( "%s: drag_dest=%p, dest_path=%p, selection_data=%p", thisfn, ( void * ) drag_dest, ( void * ) dest_path, ( void * ) selection_data );
248 
249 	return( TRUE );
250 }
251 
252 /**
253  * nact_tree_model_dnd_imulti_drag_source_drag_data_get:
254  * @context: contains
255  *  - the suggested action, as chosen by the drop site,
256  *    between those we have provided in imulti_drag_source_get_drag_actions()
257  *  - the target folder (XDS protocol)
258  * @selection_data:
259  * @rows: list of row references which are to be dropped
260  * @info: the suggested format, as chosen by the drop site, between those
261  *  we have provided in imulti_drag_source_get_target_list()
262  *
263  * This function is called when we release the selected items onto the
264  * destination
265  *
266  * NACT_XCHANGE_FORMAT_NACT:
267  * internal format for drag and drop inside the treeview:
268  * - copy in the clipboard the list of row references
269  * - selection data is empty
270  *
271  * NACT_XCHANGE_FORMAT_XDS:
272  * exchange format to drop to outside:
273  * - copy in the clipboard the list of row references
274  * - set the destination folder
275  * - selection data is 'success' or 'failure'
276  *
277  * NACT_XCHANGE_FORMAT_APPLICATION_XML:
278  * NACT_XCHANGE_FORMAT_TEXT_PLAIN:
279  * exchange format to export to outside:
280  * - do not use dnd clipboard
281  * - selection data receives the export in text format
282  *
283  * Returns: %TRUE if required data was actually provided by the source,
284  * %FALSE else.
285  */
286 gboolean
nact_tree_model_dnd_imulti_drag_source_drag_data_get(EggTreeMultiDragSource * drag_source,GdkDragContext * context,GtkSelectionData * selection_data,GList * rows,guint info)287 nact_tree_model_dnd_imulti_drag_source_drag_data_get( EggTreeMultiDragSource *drag_source,
288 				   GdkDragContext         *context,
289 				   GtkSelectionData       *selection_data,
290 				   GList                  *rows,
291 				   guint                   info )
292 {
293 	static const gchar *thisfn = "nact_tree_model_dnd_imulti_drag_source_drag_data_get";
294 	gchar *atom_name;
295 	NactTreeModel *model;
296 	gchar *data;
297 	gboolean ret = FALSE;
298 	gchar *dest_folder, *folder;
299 	gboolean is_writable;
300 	gboolean copy_data;
301 	GdkAtom selection_data_target;
302 	GdkDragAction context_suggested_action;
303 	GdkDragAction context_selected_action;
304 
305 	selection_data_target = gtk_selection_data_get_target( selection_data );
306 
307 #if GTK_CHECK_VERSION( 2, 22, 0 )
308 	context_suggested_action = gdk_drag_context_get_suggested_action( context );
309 	context_selected_action = gdk_drag_context_get_selected_action( context );
310 #else
311 	context_suggested_action = context->suggested_action;
312 	context_selected_action = context->action;
313 #endif
314 
315 	atom_name = gdk_atom_name( selection_data_target );
316 	g_debug( "%s: drag_source=%p (ref_count=%d), context=%p, suggested action=%d, selection_data=%p, rows=%p (count=%d), atom=%s",
317 			thisfn,
318 			( void * ) drag_source, G_OBJECT( drag_source )->ref_count,
319 			( void * ) context,
320 			( int ) context_suggested_action,
321 			( void * ) selection_data,
322 			( void * ) rows, g_list_length( rows ),
323 			atom_name );
324 	g_free( atom_name );
325 
326 	model = NACT_TREE_MODEL( drag_source );
327 	g_return_val_if_fail( model->private->window, FALSE );
328 
329 	if( !model->private->dispose_has_run ){
330 
331 		if( !rows || !g_list_length( rows )){
332 			return( FALSE );
333 		}
334 
335 		switch( info ){
336 			case NACT_XCHANGE_FORMAT_NACT:
337 				copy_data = ( context_selected_action == GDK_ACTION_COPY );
338 				gtk_selection_data_set( selection_data,
339 						selection_data_target, 8, ( guchar * ) "", 0 );
340 				nact_clipboard_dnd_set( model->private->clipboard, info, rows, NULL, copy_data );
341 				ret = TRUE;
342 				break;
343 
344 			case NACT_XCHANGE_FORMAT_XDS:
345 				/* get the dest default filename as an uri
346 				 * e.g. file:///home/pierre/data/eclipse/nautilus-actions/trash/xds.txt
347 				 */
348 				folder = get_xds_atom_value( context );
349 				dest_folder = g_path_get_dirname( folder );
350 
351 				/* check that target folder is writable
352 				 */
353 				is_writable = na_core_utils_dir_is_writable_uri( dest_folder );
354 				g_debug( "%s: dest_folder=%s, is_writable=%s", thisfn, dest_folder, is_writable ? "True":"False" );
355 				gtk_selection_data_set( selection_data,
356 						selection_data_target, 8, ( guchar * )( is_writable ? "S" : "F" ), 1 );
357 
358 				if( is_writable ){
359 					nact_clipboard_dnd_set( model->private->clipboard, info, rows, dest_folder, TRUE );
360 				}
361 
362 				g_free( dest_folder );
363 				g_free( folder );
364 				ret = is_writable;
365 				break;
366 
367 			case NACT_XCHANGE_FORMAT_APPLICATION_XML:
368 			case NACT_XCHANGE_FORMAT_TEXT_PLAIN:
369 				data = nact_clipboard_dnd_get_text( model->private->clipboard, rows );
370 				gtk_selection_data_set( selection_data,
371 						selection_data_target, 8, ( guchar * ) data, strlen( data ));
372 				g_free( data );
373 				ret = TRUE;
374 				break;
375 
376 			default:
377 				break;
378 		}
379 	}
380 
381 	return( ret );
382 }
383 
384 /**
385  * nact_tree_model_dnd_imulti_drag_source_drag_data_delete:
386  * @drag_source:
387  * @rows:
388  */
389 gboolean
nact_tree_model_dnd_imulti_drag_source_drag_data_delete(EggTreeMultiDragSource * drag_source,GList * rows)390 nact_tree_model_dnd_imulti_drag_source_drag_data_delete( EggTreeMultiDragSource *drag_source, GList *rows )
391 {
392 	static const gchar *thisfn = "nact_tree_model_dnd_imulti_drag_source_drag_data_delete";
393 
394 	g_debug( "%s: drag_source=%p, path_list=%p", thisfn, ( void * ) drag_source, ( void * ) rows );
395 
396 	return( TRUE );
397 }
398 
399 /**
400  * nact_tree_model_dnd_imulti_drag_source_get_drag_actions:
401  * @drag_source:
402  */
403 GdkDragAction
nact_tree_model_dnd_imulti_drag_source_get_drag_actions(EggTreeMultiDragSource * drag_source)404 nact_tree_model_dnd_imulti_drag_source_get_drag_actions( EggTreeMultiDragSource *drag_source )
405 {
406 	return( GDK_ACTION_COPY | GDK_ACTION_MOVE );
407 }
408 
409 GtkTargetList *
nact_tree_model_dnd_imulti_drag_source_get_format_list(EggTreeMultiDragSource * drag_source)410 nact_tree_model_dnd_imulti_drag_source_get_format_list( EggTreeMultiDragSource *drag_source )
411 {
412 	GtkTargetList *target_list;
413 
414 	target_list = gtk_target_list_new( dnd_source_formats, G_N_ELEMENTS( dnd_source_formats ));
415 
416 	return( target_list );
417 }
418 
419 /**
420  * nact_tree_model_dnd_imulti_drag_source_row_draggable:
421  * @drag_source:
422  * @rows:
423  *
424  * All selectable rows are draggable.
425  * Nonetheless, it's a good place to store the dragged row references.
426  * We only make use of them in on_drag_motion handler.
427  */
428 gboolean
nact_tree_model_dnd_imulti_drag_source_row_draggable(EggTreeMultiDragSource * drag_source,GList * rows)429 nact_tree_model_dnd_imulti_drag_source_row_draggable( EggTreeMultiDragSource *drag_source, GList *rows )
430 {
431 	static const gchar *thisfn = "nact_tree_model_dnd_imulti_drag_source_row_draggable";
432 	NactTreeModel *model;
433 	GtkTreeModel *store;
434 	GtkTreePath *path;
435 	GtkTreeIter iter;
436 	NAObject *object;
437 	GList *it;
438 
439 	g_debug( "%s: drag_source=%p (ref_count=%d), rows=%p (%d items)",
440 			thisfn,
441 			( void * ) drag_source, G_OBJECT( drag_source )->ref_count,
442 			( void * ) rows, g_list_length( rows ));
443 
444 	g_return_val_if_fail( NACT_IS_TREE_MODEL( drag_source ), FALSE );
445 	model = NACT_TREE_MODEL( drag_source );
446 
447 	if( !model->private->dispose_has_run ){
448 
449 		model->private->drag_has_profiles = FALSE;
450 		store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
451 
452 		for( it = rows ; it && !model->private->drag_has_profiles ; it = it->next ){
453 
454 			path = gtk_tree_row_reference_get_path(( GtkTreeRowReference * ) it->data );
455 			gtk_tree_model_get_iter( store, &iter, path );
456 			gtk_tree_model_get( store, &iter, TREE_COLUMN_NAOBJECT, &object, -1 );
457 
458 			if( NA_IS_OBJECT_PROFILE( object )){
459 				model->private->drag_has_profiles = TRUE;
460 			}
461 
462 			g_object_unref( object );
463 			gtk_tree_path_free( path );
464 		}
465 	}
466 
467 	return( TRUE );
468 }
469 
470 /**
471  * nact_tree_model_dnd_on_drag_begin:
472  * @widget: the GtkTreeView from which item is to be dragged.
473  * @context:
474  * @window: the parent #NactMainWindow instance.
475  *
476  * This function is called once, at the beginning of the drag operation,
477  * when we are dragging from the IActionsList treeview.
478  */
479 void
nact_tree_model_dnd_on_drag_begin(GtkWidget * widget,GdkDragContext * context,BaseWindow * window)480 nact_tree_model_dnd_on_drag_begin( GtkWidget *widget, GdkDragContext *context, BaseWindow *window )
481 {
482 	static const gchar *thisfn = "nact_tree_model_dnd_on_drag_begin";
483 	NactTreeModel *model;
484 	GdkWindow *context_source_window;
485 
486 	g_return_if_fail( GTK_IS_TREE_VIEW( widget ));
487 	model = ( NactTreeModel * ) gtk_tree_view_get_model( GTK_TREE_VIEW( widget ));
488 	g_return_if_fail( NACT_IS_TREE_MODEL( model ));
489 
490 	if( !model->private->dispose_has_run ){
491 
492 		g_debug( "%s: widget=%p, context=%p, window=%p, model=%p (ref_count=%d)",
493 				thisfn,
494 				( void * ) widget,
495 				( void * ) context,
496 				( void * ) window,
497 				( void * ) model, G_OBJECT( model )->ref_count );
498 
499 		model->private->drag_highlight = FALSE;
500 		model->private->drag_drop = FALSE;
501 
502 		nact_clipboard_dnd_clear( model->private->clipboard );
503 
504 #if GTK_CHECK_VERSION( 2, 22, 0 )
505 		context_source_window = gdk_drag_context_get_source_window( context );
506 #else
507 		context_source_window = context->source_window;
508 #endif
509 
510 		gdk_property_change(
511 				context_source_window,
512 				XDS_ATOM, TEXT_ATOM, 8, GDK_PROP_MODE_REPLACE, ( guchar * ) XDS_FILENAME, strlen( XDS_FILENAME ));
513 	}
514 }
515 
516 /**
517  * nact_tree_model_dnd_on_drag_end:
518  * @widget:
519  * @context:
520  * @window:
521  */
522 void
nact_tree_model_dnd_on_drag_end(GtkWidget * widget,GdkDragContext * context,BaseWindow * window)523 nact_tree_model_dnd_on_drag_end( GtkWidget *widget, GdkDragContext *context, BaseWindow *window )
524 {
525 	static const gchar *thisfn = "nact_tree_model_dnd_on_drag_end";
526 	NactTreeModel *model;
527 	GdkWindow *context_source_window;
528 
529 	g_return_if_fail( GTK_IS_TREE_VIEW( widget ));
530 	model = ( NactTreeModel * ) gtk_tree_view_get_model( GTK_TREE_VIEW( widget ));
531 	g_return_if_fail( NACT_IS_TREE_MODEL( model ));
532 
533 	if( !model->private->dispose_has_run ){
534 
535 		g_debug( "%s: widget=%p, context=%p, window=%p, model=%p (ref_count=%d)",
536 				thisfn,
537 				( void * ) widget,
538 				( void * ) context,
539 				( void * ) window,
540 				( void * ) model, G_OBJECT( model )->ref_count );
541 
542 		nact_clipboard_dnd_drag_end( model->private->clipboard );
543 		nact_clipboard_dnd_clear( model->private->clipboard );
544 
545 #if GTK_CHECK_VERSION( 2, 22, 0 )
546 		context_source_window = gdk_drag_context_get_source_window( context );
547 #else
548 		context_source_window = context->source_window;
549 #endif
550 
551 		gdk_property_delete( context_source_window, XDS_ATOM );
552 	}
553 }
554 
555 /*
556  * called when a drop occurs in the treeview for a move/copy inside of
557  * the tree
558  *
559  * Returns: %TRUE if the specified rows were successfully inserted at
560  * the given dest, %FALSE else.
561  */
562 static gboolean
drop_inside(NactTreeModel * model,GtkTreePath * dest,GtkSelectionData * selection_data)563 drop_inside( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selection_data )
564 {
565 	static const gchar *thisfn = "nact_tree_model_dnd_inside_drag_and_drop";
566 	NactApplication *application;
567 	NAUpdater *updater;
568 	NactMainWindow *main_window;
569 	NAObjectItem *parent;
570 	gboolean copy_data;
571 	GList *rows;
572 	GtkTreePath *new_dest;
573 	GtkTreePath *path;
574 	NAObject *current;
575 	NAObject *inserted;
576 	GList *object_list, *it;
577 	GtkTreeIter iter;
578 	GList *deletable;
579 	gboolean relabel;
580 	NactTreeView *items_view;
581 
582 	application = NACT_APPLICATION( base_window_get_application( model->private->window ));
583 	updater = nact_application_get_updater( application );
584 
585 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( model->private->window ), FALSE );
586 	main_window = NACT_MAIN_WINDOW( model->private->window );
587 	items_view = nact_main_window_get_items_view( main_window );
588 
589 	/*
590 	 * NACT format (may embed profiles, or not)
591 	 * 	with profiles: only valid dest is inside an action
592 	 *  without profile: only valid dest is outside (besides of) an action
593 	 */
594 	parent = NULL;
595 	rows = nact_clipboard_dnd_get_data( model->private->clipboard, &copy_data );
596 
597 	if( !is_drop_possible( model, dest, &parent )){
598 		return( FALSE );
599 	}
600 
601 	new_dest = gtk_tree_path_copy( dest );
602 	if( !copy_data ){
603 		drop_inside_move_dest( model, rows, &new_dest );
604 	}
605 
606 	g_debug( "%s: rows has %d items, copy_data=%s", thisfn, g_list_length( rows ), copy_data ? "True":"False" );
607 	object_list = NULL;
608 	for( it = rows ; it ; it = it->next ){
609 		path = gtk_tree_row_reference_get_path(( GtkTreeRowReference * ) it->data );
610 		if( path ){
611 			if( gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path )){
612 				gtk_tree_model_get( GTK_TREE_MODEL( model ), &iter, TREE_COLUMN_NAOBJECT, &current, -1 );
613 				g_object_unref( current );
614 
615 				if( copy_data ){
616 					inserted = ( NAObject * ) na_object_duplicate( current, DUPLICATE_REC );
617 					na_object_set_origin( inserted, NULL );
618 					na_object_check_status( inserted );
619 					relabel = na_updater_should_pasted_be_relabeled( updater, inserted );
620 
621 				} else {
622 					inserted = na_object_ref( current );
623 					deletable = g_list_prepend( NULL, inserted );
624 					nact_tree_ieditable_delete( NACT_TREE_IEDITABLE( items_view ), deletable, TREE_OPE_MOVE );
625 					g_list_free( deletable );
626 					relabel = FALSE;
627 				}
628 
629 				na_object_prepare_for_paste( inserted, relabel, copy_data, parent );
630 				object_list = g_list_prepend( object_list, inserted );
631 				g_debug( "%s: dropped=%s", thisfn, na_object_get_label( inserted ));
632 			}
633 			gtk_tree_path_free( path );
634 		}
635 	}
636 	object_list = g_list_reverse( object_list );
637 
638 	nact_tree_ieditable_insert_at_path( NACT_TREE_IEDITABLE( items_view ), object_list, new_dest );
639 
640 	na_object_free_items( object_list );
641 	gtk_tree_path_free( new_dest );
642 
643 	g_list_foreach( rows, ( GFunc ) gtk_tree_row_reference_free, NULL );
644 	g_list_free( rows );
645 
646 	return( TRUE );
647 }
648 
649 /*
650  * is a drop possible at given dest ?
651  *
652  * the only case where we would be led to have to modify the dest if
653  * we'd want be able to drop a profile into another profile, accepting
654  * it, actually dropping the profile just before the target
655  *
656  * -> it appears both clearer for the user interface and easyer from a
657  *    code point of view to just refuse to drop a profile into a profile
658  *
659  * so this function is just to check if a drop is possible at the given
660  * dest
661  */
662 static gboolean
is_drop_possible(NactTreeModel * model,GtkTreePath * dest,NAObjectItem ** parent)663 is_drop_possible( NactTreeModel *model, GtkTreePath *dest, NAObjectItem **parent )
664 {
665 	gboolean drop_ok;
666 	NactApplication *application;
667 	NactMainWindow *main_window;
668 	GtkTreeIter iter;
669 	NAObjectItem *parent_dest;
670 
671 	drop_ok = FALSE;
672 	parent_dest = NULL;
673 	application = NACT_APPLICATION( base_window_get_application( model->private->window ));
674 
675 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( model->private->window ), FALSE );
676 	main_window = NACT_MAIN_WINDOW( model->private->window );
677 
678 	/* if we can have an iter on given dest, then the dest already exists
679 	 * so dropped items should be of the same type that already existing
680 	 */
681 	if( gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, dest )){
682 		drop_ok = is_drop_possible_before_iter( model, &iter, main_window, &parent_dest );
683 
684 	/* inserting at the end of the list
685 	 * parent_dest is NULL
686 	 */
687 	} else if( gtk_tree_path_get_depth( dest ) == 1 ){
688 
689 		if( model->private->drag_has_profiles ){
690 			nact_main_statusbar_display_with_timeout(
691 						main_window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_refuse_drop_profile ));
692 
693 		} else {
694 			drop_ok = TRUE;
695 		}
696 
697 	/* we cannot have an iter on the dest: this means that we try to
698 	 * insert items into not-opened dest (an empty menu or an action with
699 	 * zero or one profile) : check what is the parent
700 	 */
701 	} else {
702 		drop_ok = is_drop_possible_into_dest( model, dest, main_window, &parent_dest );
703 	}
704 
705 	if( drop_ok ){
706 		drop_ok = is_parent_accept_new_children( application, main_window, parent_dest );
707 	}
708 
709 	if( drop_ok && parent ){
710 		*parent = parent_dest;
711 	}
712 
713 	return( drop_ok );
714 }
715 
716 static gboolean
is_drop_possible_before_iter(NactTreeModel * model,GtkTreeIter * iter,NactMainWindow * window,NAObjectItem ** parent)717 is_drop_possible_before_iter( NactTreeModel *model, GtkTreeIter *iter, NactMainWindow *window, NAObjectItem **parent )
718 {
719 	static const gchar *thisfn = "nact_tree_model_dnd_is_drop_possible_before_iter";
720 	gboolean drop_ok;
721 	NAObject *object;
722 
723 	drop_ok = FALSE;
724 	*parent = NULL;
725 
726 	gtk_tree_model_get( GTK_TREE_MODEL( model ), iter, TREE_COLUMN_NAOBJECT, &object, -1 );
727 	g_object_unref( object );
728 	g_debug( "%s: current object at dest is %s", thisfn, G_OBJECT_TYPE_NAME( object ));
729 
730 	if( model->private->drag_has_profiles ){
731 
732 		if( NA_IS_OBJECT_PROFILE( object )){
733 			drop_ok = TRUE;
734 			*parent = na_object_get_parent( object );
735 
736 		} else {
737 			/* unable to drop a profile here */
738 			nact_main_statusbar_display_with_timeout(
739 					window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_refuse_drop_profile ));
740 		}
741 
742 	} else if( NA_IS_OBJECT_ITEM( object )){
743 			drop_ok = TRUE;
744 			*parent = na_object_get_parent( object );
745 
746 	} else {
747 		/* unable to drop an action or a menu here */
748 		nact_main_statusbar_display_with_timeout(
749 				window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_refuse_drop_item ));
750 	}
751 
752 	return( drop_ok );
753 }
754 
755 static gboolean
is_drop_possible_into_dest(NactTreeModel * model,GtkTreePath * dest,NactMainWindow * window,NAObjectItem ** parent)756 is_drop_possible_into_dest( NactTreeModel *model, GtkTreePath *dest, NactMainWindow *window, NAObjectItem **parent )
757 {
758 	static const gchar *thisfn = "nact_tree_model_dnd_is_drop_possible_into_dest";
759 	gboolean drop_ok;
760 	GtkTreePath *path;
761 	GtkTreeIter iter;
762 	NAObject *object;
763 
764 	drop_ok = FALSE;
765 	*parent = NULL;
766 
767 	path = gtk_tree_path_copy( dest );
768 
769 	if( gtk_tree_path_up( path )){
770 		if( gtk_tree_model_get_iter( GTK_TREE_MODEL( model ), &iter, path )){
771 			gtk_tree_model_get( GTK_TREE_MODEL( model ), &iter, TREE_COLUMN_NAOBJECT, &object, -1 );
772 			g_object_unref( object );
773 			g_debug( "%s: current object at parent dest is %s", thisfn, G_OBJECT_TYPE_NAME( object ));
774 
775 			if( model->private->drag_has_profiles ){
776 
777 				if( NA_IS_OBJECT_ACTION( object )){
778 					drop_ok = TRUE;
779 					*parent = NA_OBJECT_ITEM( object );
780 
781 				} else {
782 					nact_main_statusbar_display_with_timeout(
783 							window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_refuse_drop_profile ));
784 				}
785 
786 			} else if( NA_IS_OBJECT_MENU( object )){
787 					drop_ok = TRUE;
788 					*parent = na_object_get_parent( object );
789 
790 			} else {
791 				nact_main_statusbar_display_with_timeout(
792 						window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_refuse_drop_item ));
793 			}
794 		}
795 	}
796 
797 	gtk_tree_path_free( path );
798 
799 	return( drop_ok );
800 }
801 
802 static void
drop_inside_move_dest(NactTreeModel * model,GList * rows,GtkTreePath ** dest)803 drop_inside_move_dest( NactTreeModel *model, GList *rows, GtkTreePath **dest )
804 {
805 	GList *it;
806 	GtkTreePath *path;
807 	gint before;
808 	gint i;
809 	gint *indices;
810 
811 	g_return_if_fail( dest );
812 
813 	before = 0;
814 	for( it = rows ; it ; it = it->next ){
815 		path = gtk_tree_row_reference_get_path(( GtkTreeRowReference * ) it->data );
816 		if( path ){
817 			if( gtk_tree_path_get_depth( path ) == 1 && gtk_tree_path_compare( path, *dest ) == -1 ){
818 				before += 1;
819 			}
820 			gtk_tree_path_free( path );
821 		}
822 	}
823 
824 	g_debug( "drop_inside_move_dest: before=%d", before );
825 	g_debug( "drop_inside_move_dest: dest=%s", gtk_tree_path_to_string( *dest ));
826 
827 	if( before ){
828 		indices = gtk_tree_path_get_indices( *dest );
829 		indices[0] -= before;
830 		path = gtk_tree_path_new_from_indices( indices[0], -1 );
831 		for( i = 1 ; i < gtk_tree_path_get_depth( *dest ); ++i ){
832 			gtk_tree_path_append_index( path, indices[i] );
833 		}
834 		gtk_tree_path_free( *dest );
835 		*dest = gtk_tree_path_copy( path );
836 		gtk_tree_path_free( path );
837 	}
838 
839 	g_debug( "drop_inside_move_dest: dest=%s", gtk_tree_path_to_string( *dest ));
840 }
841 
842 /*
843  * called when a drop from the outside occurs in the treeview
844  *
845  * Returns: %TRUE if the specified rows were successfully inserted at
846  * the given dest, %FALSE else.
847  *
848  * URI format only involves actions or menus
849  *  so ony valid dest in outside (besides of) an action
850  */
851 static gboolean
drop_uri_list(NactTreeModel * model,GtkTreePath * dest,GtkSelectionData * selection_data)852 drop_uri_list( NactTreeModel *model, GtkTreePath *dest, GtkSelectionData  *selection_data )
853 {
854 	static const gchar *thisfn = "nact_tree_model_dnd_drop_uri_list";
855 	gboolean drop_done;
856 	NactApplication *application;
857 	NAUpdater *updater;
858 	NactMainWindow *main_window;
859 	NAImporterParms parms;
860 	GList *import_results, *it;
861 	guint count;
862 	GSList *im;
863 	GList *imported, *overriden;
864 	const gchar *selection_data_data;
865 	NactTreeView *view;
866 	GSList *messages;
867 	gchar *dlg_message;
868 	GtkWidget *dialog;
869 
870 	gchar *dest_str = gtk_tree_path_to_string( dest );
871 	g_debug( "%s: model=%p, dest=%p (%s), selection_data=%p",
872 			thisfn, ( void * ) model, ( void * ) dest, dest_str, ( void * ) selection_data );
873 	g_free( dest_str );
874 
875 	drop_done = FALSE;
876 	model->private->drag_has_profiles = FALSE;
877 
878 	if( !is_drop_possible( model, dest, NULL )){
879 		return( FALSE );
880 	}
881 
882 	application = NACT_APPLICATION( base_window_get_application( model->private->window ));
883 	updater = nact_application_get_updater( application );
884 
885 	g_return_val_if_fail( NACT_IS_MAIN_WINDOW( model->private->window ), FALSE );
886 	main_window = NACT_MAIN_WINDOW( model->private->window );
887 
888 	selection_data_data = ( const gchar * ) gtk_selection_data_get_data( selection_data );
889 	g_debug( "%s", selection_data_data );
890 
891 	memset( &parms, '\0', sizeof( NAImporterParms ));
892 	parms.uris = g_slist_reverse( na_core_utils_slist_from_split( selection_data_data, "\r\n" ));
893 	parms.check_fn = ( NAImporterCheckFn ) is_dropped_already_exists;
894 	parms.check_fn_data = main_window;
895 	parms.preferred_mode = 0;
896 	parms.parent_toplevel = base_window_get_gtk_toplevel( BASE_WINDOW( main_window ));
897 
898 	import_results = na_importer_import_from_uris( NA_PIVOT( updater ), &parms );
899 
900 	/* analysing output results, simultaneously building a concatenation
901 	 * of all lines of messages, and the list of imported items
902 	 */
903 	imported = NULL;
904 	overriden = NULL;
905 	messages = NULL;
906 
907 	for( it = import_results ; it ; it = it->next ){
908 		NAImporterResult *result = ( NAImporterResult * ) it->data;
909 
910 		for( im = result->messages ; im ; im = im->next ){
911 			messages = g_slist_prepend( messages, im->data );
912 		}
913 		if( result->imported ){
914 			if( !result->exist || result->mode == IMPORTER_MODE_RENUMBER ){
915 				imported = g_list_prepend( imported, result->imported );
916 				na_updater_check_item_writability_status( updater, result->imported );
917 
918 			} else if( result->mode == IMPORTER_MODE_OVERRIDE ){
919 				overriden = g_list_prepend( overriden, result->imported );
920 			}
921 		}
922 	}
923 
924 	/* if there is more than one message, display them in a dialog box
925 	 * else in the status bar
926 	 */
927 	count = g_slist_length( messages );
928 	g_debug( "%s: count=%d", thisfn, count );
929 	if( count == 1 ){
930 		nact_main_statusbar_display_with_timeout(
931 				main_window, TREE_MODEL_STATUSBAR_CONTEXT, messages->data );
932 	}
933 	if( count > 1 ){
934 		dlg_message = na_core_utils_slist_join_at_end( messages, "\n" );
935 		g_debug( "%s: dlg_message='%s'", thisfn, dlg_message );
936 		dialog = gtk_message_dialog_new(
937 				parms.parent_toplevel,
938 				GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE,
939 				"%s", _( "Some messages have occurred during drop operation." ));
940 		gtk_message_dialog_format_secondary_markup( GTK_MESSAGE_DIALOG( dialog ), "%s", dlg_message );
941 		gtk_dialog_run( GTK_DIALOG( dialog ));
942 		gtk_widget_destroy( dialog );
943 		g_free( dlg_message );
944 	}
945 	g_slist_free( messages );
946 
947 	/* insert newly imported items in the list view
948 	 */
949 	if( imported ){
950 		na_object_dump_tree( imported );
951 		view = nact_main_window_get_items_view( main_window );
952 		nact_tree_ieditable_insert_at_path( NACT_TREE_IEDITABLE( view ), imported, dest );
953 	}
954 
955 	/* override items if needed
956 	 * they may safely be released after having updated the store
957 	 */
958 	if( overriden ){
959 		na_object_dump_tree( overriden );
960 		view = nact_main_window_get_items_view( main_window );
961 		nact_tree_ieditable_set_items( NACT_TREE_IEDITABLE( view ), overriden );
962 		na_object_free_items( overriden );
963 	}
964 
965 	drop_done = TRUE;
966 	na_object_free_items( imported );
967 	na_object_free_items( overriden );
968 	na_core_utils_slist_free( parms.uris );
969 
970 	for( it = import_results ; it ; it = it->next ){
971 		na_importer_free_result( it->data );
972 	}
973 	g_list_free( import_results );
974 
975 	return( drop_done );
976 }
977 
978 static NAObjectItem *
is_dropped_already_exists(const NAObjectItem * importing,const NactMainWindow * window)979 is_dropped_already_exists( const NAObjectItem *importing, const NactMainWindow *window )
980 {
981 	NactTreeView *items_view;
982 
983 	gchar *id = na_object_get_id( importing );
984 	items_view = nact_main_window_get_items_view( window );
985 	NAObjectItem *exists = nact_tree_view_get_item_by_id( items_view, id );
986 	g_free( id );
987 
988 	return( exists );
989 }
990 
991 /*
992  * this function works well, but only called from on_drag_motion handler...
993  */
994 /*static gboolean
995 is_row_drop_possible( NactTreeModel *model, GtkTreePath *path, GtkTreeViewDropPosition pos )
996 {
997 	gboolean ok = FALSE;
998 	GtkTreeModel *store;
999 	GtkTreeIter iter;
1000 	NAObject *object;
1001 
1002 	store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
1003 	gtk_tree_model_get_iter( store, &iter, path );
1004 	gtk_tree_model_get( store, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
1005 
1006 	if( model->private->drag_has_profiles ){
1007 		if( NA_IS_OBJECT_PROFILE( object )){
1008 			ok = TRUE;
1009 		} else if( NA_IS_OBJECT_ACTION( object )){
1010 			ok = ( pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER );
1011 		}
1012 	} else {
1013 		if( NA_IS_OBJECT_ITEM( object )){
1014 			ok = TRUE;
1015 		}
1016 	}
1017 
1018 	g_object_unref( object );
1019 	return( ok );
1020 }*/
1021 
1022 /*
1023  * called when the user moves into the target widget
1024  * returns TRUE if a drop zone
1025  */
1026 #if 0
1027 static gboolean
1028 on_drag_motion( GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, BaseWindow *window )
1029 {
1030 	NactTreeModel *model;
1031 	gboolean ok = FALSE;
1032 	GtkTreePath *path;
1033 	GtkTreeViewDropPosition pos;
1034 
1035 	model = NACT_TREE_MODEL( gtk_tree_view_get_model( GTK_TREE_VIEW( widget )));
1036 	g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), FALSE );
1037 
1038 	if( !model->private->dispose_has_run ){
1039 
1040 		if( !model->private->drag_highlight ){
1041 			model->private->drag_highlight = TRUE;
1042 			gtk_drag_highlight( widget );
1043 		}
1044 
1045 		/*target = gtk_drag_dest_find_target( widget, context, NULL );
1046 		if( target == GDK_NONE ){
1047 			gdk_drag_status( context, 0, time );
1048 		} else {
1049 			gtk_drag_get_data( widget, context, target, time );
1050 		}*/
1051 
1052 		if( gtk_tree_view_get_dest_row_at_pos( GTK_TREE_VIEW( widget ), x, y, &path, &pos )){
1053 			ok = is_row_drop_possible( model, path, pos );
1054 			if( ok ){
1055 				gdk_drag_status( context, 0, time );
1056 			}
1057 		}
1058 
1059 		gtk_tree_path_free( path );
1060 		g_debug( "nact_tree_model_on_drag_motion: ok=%s, pos=%d", ok ? "True":"False", pos );
1061 	}
1062 
1063 	return( ok );
1064 }
1065 #endif
1066 
1067 /*
1068  * called when the user drops the data
1069  * returns TRUE if a drop zone
1070  */
1071 /*static gboolean
1072 on_drag_drop( GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, BaseWindow *window )
1073 {
1074 	NactTreeModel *model;
1075 
1076 	model = NACT_TREE_MODEL( gtk_tree_view_get_model( GTK_TREE_VIEW( widget )));
1077 	g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), FALSE );
1078 
1079 	if( !model->private->dispose_has_run ){
1080 
1081 		model->private->drag_drop = TRUE;
1082 	}
1083 
1084 	return( TRUE );
1085 }*/
1086 
1087 /* The following function taken from bugzilla
1088  * (http://bugzilla.gnome.org/attachment.cgi?id=49362&action=view)
1089  * Author: Christian Neumair
1090  * Copyright: 2005 Free Software Foundation, Inc
1091  * License: GPL
1092  *
1093  * On a 32-bits system:
1094  * get_xds_atom_value: actual_length=63, actual_length=15
1095  * get_xds_atom_value: ret=file:///home/pierre/data/eclipse/nautilus-actions/trash/xds.txt0x8299
1096  * get_xds_atom_value: dup=file:///home/pierre/data/eclipse/nautilus-actions/trash/xds.txt
1097  * get_xds_atom_value: ret=file:///home/pi
1098  *
1099  * idem on a 64bits system.
1100  */
1101 static char *
get_xds_atom_value(GdkDragContext * context)1102 get_xds_atom_value( GdkDragContext *context )
1103 {
1104 	gchar *ret;
1105 	gint actual_length;
1106 	GdkWindow *context_source_window;
1107 
1108 #if GTK_CHECK_VERSION( 2, 22, 0 )
1109 		context_source_window = gdk_drag_context_get_source_window( context );
1110 #else
1111 		context_source_window = context->source_window;
1112 #endif
1113 
1114 	g_return_val_if_fail( context != NULL, NULL );
1115 	g_return_val_if_fail( context_source_window != NULL, NULL );
1116 
1117 	gdk_property_get( context_source_window,		/* a GdkWindow */
1118 						XDS_ATOM, 					/* the property to retrieve */
1119 						TEXT_ATOM,					/* the desired property type */
1120 						0, 							/* offset (in 4 bytes chunks) */
1121 						MAX_XDS_ATOM_VAL_LEN,		/* max length (in 4 bytes chunks) */
1122 						FALSE, 						/* whether to delete the property */
1123 						NULL, 						/* actual property type */
1124 						NULL, 						/* actual format */
1125 						&actual_length,				/* actual length (in 4 bytes chunks) */
1126 						( guchar ** ) &ret );		/* data pointer */
1127 
1128 	ret[actual_length] = '\0';
1129 
1130 	return( ret );
1131 }
1132 
1133 /*
1134  * when dropping something somewhere, we must ensure that we will be able
1135  * to register the new child
1136  */
1137 static gboolean
is_parent_accept_new_children(NactApplication * application,NactMainWindow * window,NAObjectItem * parent)1138 is_parent_accept_new_children( NactApplication *application, NactMainWindow *window, NAObjectItem *parent )
1139 {
1140 	gboolean accept_ok;
1141 	NAUpdater *updater;
1142 
1143 	accept_ok = FALSE;
1144 	updater = nact_application_get_updater( application );
1145 
1146 	/* inserting as a level zero item
1147 	 * ensure that level zero is writable
1148 	 */
1149 	if( parent == NULL ){
1150 		if( na_updater_is_level_zero_writable( updater )){
1151 			accept_ok = TRUE;
1152 
1153 		} else {
1154 			nact_main_statusbar_display_with_timeout(
1155 						window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_level_zero_not_writable ));
1156 		}
1157 
1158 	/* see if the parent is writable
1159 	 */
1160 	} else if( na_object_is_finally_writable( parent, NULL )){
1161 		accept_ok = TRUE;
1162 
1163 	} else {
1164 			nact_main_statusbar_display_with_timeout(
1165 						window, TREE_MODEL_STATUSBAR_CONTEXT, gettext( st_parent_not_writable ));
1166 	}
1167 
1168 	return( accept_ok );
1169 }
1170 
1171 static guint
target_atom_to_id(GdkAtom atom)1172 target_atom_to_id( GdkAtom atom )
1173 {
1174 	gint i;
1175 	guint info = 0;
1176 	gchar *atom_name;
1177 
1178 	atom_name = gdk_atom_name( atom );
1179 	for( i = 0 ; i < tree_model_dnd_dest_formats_count ; ++i ){
1180 		if( !g_ascii_strcasecmp( tree_model_dnd_dest_formats[i].target, atom_name )){
1181 			info = tree_model_dnd_dest_formats[i].info;
1182 			break;
1183 		}
1184 	}
1185 	g_free( atom_name );
1186 	return( info );
1187 }
1188