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, ©_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, ¤t, -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