/* * fm-dnd-src.c * * Copyright 2009 PCMan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "fm-dnd-src.h" GtkTargetEntry fm_default_dnd_src_targets[] = { {"application/x-fmlist-ptr", GTK_TARGET_SAME_APP, FM_DND_SRC_TARGET_FM_LIST}, {"text/uri-list", 0, FM_DND_SRC_TARGET_URI_LIST} }; enum { DATA_GET, N_SIGNALS }; static void fm_dnd_src_finalize (GObject *object); static void on_drag_data_get ( GtkWidget *src_widget, GdkDragContext *drag_context, GtkSelectionData *sel_data, guint info, guint time, FmDndSrc* ds ); static void on_drag_begin ( GtkWidget *src_widget, GdkDragContext *drag_context, FmDndSrc* ds ); static void on_drag_end ( GtkWidget *src_widget, GdkDragContext *drag_context, FmDndSrc* ds ); static guint signals[N_SIGNALS]; G_DEFINE_TYPE(FmDndSrc, fm_dnd_src, G_TYPE_OBJECT); static void fm_dnd_src_class_init(FmDndSrcClass *klass) { GObjectClass *g_object_class; FmDndSrcClass *dnd_src_class; g_object_class = G_OBJECT_CLASS(klass); g_object_class->finalize = fm_dnd_src_finalize; dnd_src_class = FM_DND_SRC_CLASS(klass); /* emitted when information of source files is needed. * call fm_dnd_source_set_files() in its callback to * provide info of dragged source files. */ signals[ DATA_GET ] = g_signal_new ( "data-get", G_TYPE_FROM_CLASS( klass ), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET ( FmDndSrcClass, data_get ), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void fm_dnd_src_finalize(GObject *object) { FmDndSrc *ds; g_return_if_fail(object != NULL); g_return_if_fail(FM_IS_DND_SRC(object)); ds = FM_DND_SRC(object); if(ds->files) fm_list_unref(ds->files); fm_dnd_src_set_widget(ds, NULL); G_OBJECT_CLASS(fm_dnd_src_parent_class)->finalize(object); } static void fm_dnd_src_init(FmDndSrc *self) { } FmDndSrc *fm_dnd_src_new(GtkWidget* w) { FmDndSrc* ds = (FmDndSrc*)g_object_new(FM_TYPE_DND_SRC, NULL); fm_dnd_src_set_widget(ds, w); return ds; } void fm_dnd_src_set_widget(FmDndSrc* ds, GtkWidget* w) { if(w == ds->widget) return; if(ds->widget) /* there is an old widget connected */ { g_signal_handlers_disconnect_by_func(ds->widget, on_drag_data_get, ds); g_signal_handlers_disconnect_by_func(ds->widget, on_drag_begin, ds); g_signal_handlers_disconnect_by_func(ds->widget, on_drag_end, ds); } ds->widget = w; if( w ) { g_object_add_weak_pointer(G_OBJECT(w), &ds->widget); g_signal_connect(w, "drag-data-get", G_CALLBACK(on_drag_data_get), ds); g_signal_connect_after(w, "drag-begin", G_CALLBACK(on_drag_begin), ds); g_signal_connect_after(w, "drag-end", G_CALLBACK(on_drag_end), ds); } } void fm_dnd_src_set_files(FmDndSrc* ds, FmFileInfoList* files) { ds->files = fm_list_ref(files); } void fm_dnd_src_set_file(FmDndSrc* ds, FmFileInfo* file) { FmFileInfoList* files = fm_file_info_list_new(); fm_list_push_tail(files, file); ds->files = files; } static void on_drag_data_get ( GtkWidget *src_widget, GdkDragContext *drag_context, GtkSelectionData *sel_data, guint info, guint time, FmDndSrc* ds ) { GdkAtom type; /* Don't call the default handler */ g_signal_stop_emission_by_name( src_widget, "drag-data-get" ); drag_context->actions = GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK; type = gdk_atom_intern_static_string(fm_default_dnd_src_targets[info].target); switch( info ) { case FM_DND_SRC_TARGET_FM_LIST: /* just store the pointer in GtkSelection since this is used * within the same app. */ gtk_selection_data_set(sel_data, type, 8, &ds->files, sizeof(gpointer)); break; case FM_DND_SRC_TARGET_URI_LIST: { gchar* uri; GString* uri_list = g_string_sized_new( 8192 ); GList* l; FmFileInfo* file; char* full_path; for( l = fm_list_peek_head_link(ds->files); l; l=l->next ) { file = (FmFileInfo*)l->data; uri = fm_path_to_uri(file->path); g_string_append( uri_list, uri ); g_free( uri ); g_string_append( uri_list, "\r\n" ); } gtk_selection_data_set ( sel_data, type, 8, ( guchar* ) uri_list->str, uri_list->len + 1 ); g_string_free( uri_list, TRUE ); } break; } } static void on_drag_begin ( GtkWidget *src_widget, GdkDragContext *drag_context, FmDndSrc* ds ) { /* block default handler */ gtk_drag_set_icon_default( drag_context ); /* FIXME: set the icon to file icon later */ // gtk_drag_set_icon_pixbuf(); /* ask drag source to provide list of source files. */ g_signal_emit(ds, signals[DATA_GET], 0); } static void on_drag_end ( GtkWidget *src_widget, GdkDragContext *drag_context, FmDndSrc* ds ) { }