1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/dnd.cpp
3 // Purpose:     wxDropTarget class
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #if wxUSE_DRAG_AND_DROP
13 
14 #include "wx/dnd.h"
15 
16 #ifndef WX_PRECOMP
17     #include "wx/intl.h"
18     #include "wx/log.h"
19     #include "wx/app.h"
20     #include "wx/utils.h"
21     #include "wx/window.h"
22     #include "wx/gdicmn.h"
23 #endif
24 
25 #include "wx/scopeguard.h"
26 
27 #include "wx/gtk/private/wrapgtk.h"
28 
29 //----------------------------------------------------------------------------
30 // global data
31 //----------------------------------------------------------------------------
32 
33 extern bool g_blockEventsOnDrag;
34 
35 // the flags used for the last DoDragDrop()
36 static long gs_flagsForDrag = 0;
37 
38 // the trace mask we use with wxLogTrace() - call
39 // wxLog::AddTraceMask(TRACE_DND) to enable the trace messages from here
40 // (there are quite a few of them, so don't enable this by default)
41 #define TRACE_DND "dnd"
42 
43 // global variables because GTK+ DnD want to have the
44 // mouse event that caused it
45 extern GdkEvent *g_lastMouseEvent;
46 extern int       g_lastButtonNumber;
47 
48 //----------------------------------------------------------------------------
49 // standard icons
50 //----------------------------------------------------------------------------
51 
52 /* Copyright (c) Julian Smart */
53 static const char * page_xpm[] = {
54 /* columns rows colors chars-per-pixel */
55 "32 32 37 1",
56 "5 c #7198D9",
57 ", c #769CDA",
58 "2 c #DCE6F6",
59 "i c #FFFFFF",
60 "e c #779DDB",
61 ": c #9AB6E4",
62 "9 c #EAF0FA",
63 "- c #B1C7EB",
64 "$ c #6992D7",
65 "y c #F7F9FD",
66 "= c #BED0EE",
67 "q c #F0F5FC",
68 "; c #A8C0E8",
69 "@ c #366BC2",
70 "  c None",
71 "u c #FDFEFF",
72 "8 c #5987D3",
73 "* c #C4D5F0",
74 "7 c #7CA0DC",
75 "O c #487BCE",
76 "< c #6B94D7",
77 "& c #CCDAF2",
78 "> c #89A9DF",
79 "3 c #5584D1",
80 "w c #82A5DE",
81 "1 c #3F74CB",
82 "+ c #3A70CA",
83 ". c #3569BF",
84 "% c #D2DFF4",
85 "# c #3366BB",
86 "r c #F5F8FD",
87 "0 c #FAFCFE",
88 "4 c #DFE8F7",
89 "X c #5E8AD4",
90 "o c #5282D0",
91 "t c #B8CCEC",
92 "6 c #E5EDF9",
93 /* pixels */
94 "                                ",
95 "                                ",
96 "                                ",
97 "                                ",
98 "                                ",
99 "       .XXXooOO++@#             ",
100 "       $%&*=-;::>,<1            ",
101 "       $2%&*=-;::><:3           ",
102 "       $42%&*=-;::<&:3          ",
103 "       56477<<<<8<<9&:X         ",
104 "       59642%&*=-;<09&:5        ",
105 "       5q9642%&*=-<<<<<#        ",
106 "       5qqw777<<<<<88:>+        ",
107 "       erqq9642%&*=t;::+        ",
108 "       eyrqq9642%&*=t;:O        ",
109 "       eyywwww777<<<<t;O        ",
110 "       e0yyrqq9642%&*=to        ",
111 "       e00yyrqq9642%&*=o        ",
112 "       eu0wwwwwww777<&*X        ",
113 "       euu00yyrqq9642%&X        ",
114 "       eiuu00yyrqq9642%X        ",
115 "       eiiwwwwwwwwww742$        ",
116 "       eiiiuu00yyrqq964$        ",
117 "       eiiiiuu00yyrqq96$        ",
118 "       eiiiiiuu00yyrqq95        ",
119 "       eiiiiiiuu00yyrqq5        ",
120 "       eeeeeeeeeeeeee55e        ",
121 "                                ",
122 "                                ",
123 "                                ",
124 "                                ",
125 "                                "
126 };
127 
128 
129 // ============================================================================
130 // private functions
131 // ============================================================================
132 
133 // ----------------------------------------------------------------------------
134 // convert between GTK+ and wxWidgets DND constants
135 // ----------------------------------------------------------------------------
136 
ConvertFromGTK(long action)137 static wxDragResult ConvertFromGTK(long action)
138 {
139     switch ( action )
140     {
141         case GDK_ACTION_COPY:
142             return wxDragCopy;
143 
144         case GDK_ACTION_LINK:
145             return wxDragLink;
146 
147         case GDK_ACTION_MOVE:
148             return wxDragMove;
149     }
150 
151     return wxDragNone;
152 }
153 
154 // ----------------------------------------------------------------------------
155 // "drag_leave"
156 // ----------------------------------------------------------------------------
157 
158 extern "C" {
target_drag_leave(GtkWidget * WXUNUSED (widget),GdkDragContext * context,guint WXUNUSED (time),wxDropTarget * drop_target)159 static void target_drag_leave( GtkWidget *WXUNUSED(widget),
160                                GdkDragContext *context,
161                                guint WXUNUSED(time),
162                                wxDropTarget *drop_target )
163 {
164     /* inform the wxDropTarget about the current GdkDragContext.
165        this is only valid for the duration of this call */
166     drop_target->GTKSetDragContext( context );
167 
168     /* we don't need return values. this event is just for
169        information */
170     drop_target->OnLeave();
171 
172     /* this has to be done because GDK has no "drag_enter" event */
173     drop_target->m_firstMotion = true;
174 
175     /* after this, invalidate the drop_target's GdkDragContext */
176     drop_target->GTKSetDragContext( NULL );
177 }
178 }
179 
180 // ----------------------------------------------------------------------------
181 // "drag_motion"
182 // ----------------------------------------------------------------------------
183 
184 extern "C" {
target_drag_motion(GtkWidget * WXUNUSED (widget),GdkDragContext * context,gint x,gint y,guint time,wxDropTarget * drop_target)185 static gboolean target_drag_motion( GtkWidget *WXUNUSED(widget),
186                                     GdkDragContext *context,
187                                     gint x,
188                                     gint y,
189                                     guint time,
190                                     wxDropTarget *drop_target )
191 {
192     /* Owen Taylor: "if the coordinates not in a drop zone,
193        return FALSE, otherwise call gtk_drag_status() and
194        return TRUE" */
195 
196 #if 0
197     wxPrintf( "motion\n" );
198     GList *tmp_list;
199     for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
200     {
201         wxString atom = wxString::FromAscii( gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)) );
202         wxPrintf( "Atom: %s\n", atom );
203     }
204 #endif
205 
206     // Inform the wxDropTarget about the current GdkDragContext.
207     // This is only valid for the duration of this call.
208     drop_target->GTKSetDragContext( context );
209 
210     // Does the source actually accept the data type?
211     if (drop_target->GTKGetMatchingPair() == (GdkAtom) 0)
212     {
213         drop_target->GTKSetDragContext( NULL );
214         return FALSE;
215     }
216 
217     wxDragResult suggested_action = drop_target->GTKFigureOutSuggestedAction();
218 
219     wxDragResult result = wxDragNone;
220 
221     if (drop_target->m_firstMotion)
222     {
223         // the first "drag_motion" event substitutes a "drag_enter" event
224         result = drop_target->OnEnter( x, y, suggested_action );
225     }
226     else
227     {
228         // give program a chance to react (i.e. to say no by returning FALSE)
229         result = drop_target->OnDragOver( x, y, suggested_action );
230     }
231 
232     GdkDragAction result_action = GDK_ACTION_DEFAULT;
233     if (result == wxDragCopy)
234         result_action = GDK_ACTION_COPY;
235     else if (result == wxDragLink)
236         result_action = GDK_ACTION_LINK;
237     else
238         result_action = GDK_ACTION_MOVE;
239 
240     // is result action actually supported
241     bool ret = (result_action != GDK_ACTION_DEFAULT) &&
242                (gdk_drag_context_get_actions(context) & result_action);
243 
244     if (ret)
245         gdk_drag_status( context, result_action, time );
246 
247     // after this, invalidate the drop_target's GdkDragContext
248     drop_target->GTKSetDragContext( NULL );
249 
250     // this has to be done because GDK has no "drag_enter" event
251     drop_target->m_firstMotion = false;
252 
253     return ret;
254 }
255 }
256 
257 // ----------------------------------------------------------------------------
258 // "drag_drop"
259 // ----------------------------------------------------------------------------
260 
261 extern "C" {
target_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,wxDropTarget * drop_target)262 static gboolean target_drag_drop( GtkWidget *widget,
263                                   GdkDragContext *context,
264                                   gint x,
265                                   gint y,
266                                   guint time,
267                                   wxDropTarget *drop_target )
268 {
269     /* Owen Taylor: "if the drop is not in a drop zone,
270        return FALSE, otherwise, if you aren't accepting
271        the drop, call gtk_drag_finish() with success == FALSE
272        otherwise call gtk_drag_data_get()" */
273 
274     /* inform the wxDropTarget about the current GdkDragContext.
275        this is only valid for the duration of this call */
276     drop_target->GTKSetDragContext( context );
277 
278     // Does the source actually accept the data type?
279     if (drop_target->GTKGetMatchingPair() == (GdkAtom) 0)
280     {
281         // cancel the whole thing
282         gtk_drag_finish( context,
283                           FALSE,        // no success
284                           FALSE,        // don't delete data on dropping side
285                           time );
286 
287         drop_target->GTKSetDragContext( NULL );
288 
289         drop_target->m_firstMotion = true;
290 
291         return FALSE;
292     }
293 
294     /* inform the wxDropTarget about the current drag widget.
295        this is only valid for the duration of this call */
296     drop_target->GTKSetDragWidget( widget );
297 
298     /* inform the wxDropTarget about the current drag time.
299        this is only valid for the duration of this call */
300     drop_target->GTKSetDragTime( time );
301 
302     /* reset the block here as someone might very well
303        show a dialog as a reaction to a drop and this
304        wouldn't work without events */
305     g_blockEventsOnDrag = false;
306 
307     bool ret = drop_target->OnDrop( x, y );
308 
309     if (!ret)
310     {
311         wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned FALSE") );
312 
313         /* cancel the whole thing */
314         gtk_drag_finish( context,
315                           FALSE,        /* no success */
316                           FALSE,        /* don't delete data on dropping side */
317                           time );
318     }
319     else
320     {
321         wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned true") );
322 
323         GdkAtom format = drop_target->GTKGetMatchingPair();
324 
325         // this does happen somehow, see bug 555111
326         wxCHECK_MSG( format, FALSE, wxT("no matching GdkAtom for format?") );
327 
328         /* this should trigger an "drag_data_received" event */
329         gtk_drag_get_data( widget,
330                            context,
331                            format,
332                            time );
333     }
334 
335     /* after this, invalidate the drop_target's GdkDragContext */
336     drop_target->GTKSetDragContext( NULL );
337 
338     /* after this, invalidate the drop_target's drag widget */
339     drop_target->GTKSetDragWidget( NULL );
340 
341     /* this has to be done because GDK has no "drag_enter" event */
342     drop_target->m_firstMotion = true;
343 
344     return ret;
345 }
346 }
347 
348 // ----------------------------------------------------------------------------
349 // "drag_data_received"
350 // ----------------------------------------------------------------------------
351 
352 extern "C" {
target_drag_data_received(GtkWidget * WXUNUSED (widget),GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint WXUNUSED (info),guint time,wxDropTarget * drop_target)353 static void target_drag_data_received( GtkWidget *WXUNUSED(widget),
354                                        GdkDragContext *context,
355                                        gint x,
356                                        gint y,
357                                        GtkSelectionData *data,
358                                        guint WXUNUSED(info),
359                                        guint time,
360                                        wxDropTarget *drop_target )
361 {
362     /* Owen Taylor: "call gtk_drag_finish() with
363        success == TRUE" */
364 
365     if (gtk_selection_data_get_length(data) <= 0 || gtk_selection_data_get_format(data) != 8)
366     {
367         /* negative data length and non 8-bit data format
368            qualifies for junk */
369         gtk_drag_finish (context, FALSE, FALSE, time);
370 
371         return;
372     }
373 
374     wxLogTrace(TRACE_DND, wxT( "Drop target: data received event") );
375 
376     /* inform the wxDropTarget about the current GtkSelectionData.
377        this is only valid for the duration of this call */
378     drop_target->GTKSetDragData( data );
379 
380     wxDragResult result = ConvertFromGTK(gdk_drag_context_get_selected_action(context));
381 
382     if ( wxIsDragResultOk( drop_target->OnData( x, y, result ) ) )
383     {
384         wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned true") );
385 
386         /* tell GTK that data transfer was successful */
387         gtk_drag_finish( context, TRUE, FALSE, time );
388     }
389     else
390     {
391         wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned FALSE") );
392 
393         /* tell GTK that data transfer was not successful */
394         gtk_drag_finish( context, FALSE, FALSE, time );
395     }
396 
397     /* after this, invalidate the drop_target's drag data */
398     drop_target->GTKSetDragData( NULL );
399 }
400 }
401 
402 //----------------------------------------------------------------------------
403 // wxDropTarget
404 //----------------------------------------------------------------------------
405 
wxDropTarget(wxDataObject * data)406 wxDropTarget::wxDropTarget( wxDataObject *data )
407             : wxDropTargetBase( data )
408 {
409     m_firstMotion = true;
410     m_dragContext = NULL;
411     m_dragWidget = NULL;
412     m_dragData = NULL;
413     m_dragTime = 0;
414 }
415 
OnDragOver(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y),wxDragResult def)416 wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
417                                        wxCoord WXUNUSED(y),
418                                        wxDragResult def )
419 {
420     return def;
421 }
422 
OnDrop(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y))423 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
424 {
425     return true;
426 }
427 
OnData(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y),wxDragResult def)428 wxDragResult wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
429                                    wxDragResult def )
430 {
431     return GetData() ? def : wxDragNone;
432 }
433 
GTKFigureOutSuggestedAction()434 wxDragResult wxDropTarget::GTKFigureOutSuggestedAction()
435 {
436     if (!m_dragContext)
437         return wxDragError;
438 
439     // GTK+ always supposes that we want to copy the data by default while we
440     // might want to move it, so examine not only suggested_action - which is
441     // only good if we don't have our own preferences - but also the actions
442     // field
443     wxDragResult suggested_action = wxDragNone;
444     const GdkDragAction actions = gdk_drag_context_get_actions(m_dragContext);
445     if (GetDefaultAction() == wxDragNone)
446     {
447         // use default action set by wxDropSource::DoDragDrop()
448         if ( (gs_flagsForDrag & wxDrag_DefaultMove) == wxDrag_DefaultMove &&
449             (actions & GDK_ACTION_MOVE))
450         {
451             // move is requested by the program and allowed by GTK+ - do it, even
452             // though suggested_action may be currently wxDragCopy
453             suggested_action = wxDragMove;
454         }
455         else // use whatever GTK+ says we should
456         {
457             suggested_action = ConvertFromGTK(gdk_drag_context_get_suggested_action(m_dragContext));
458 
459 #if 0
460             // RR: I don't understand the code below: if the drag comes from
461             //     a different app, the gs_flagsForDrag is invalid; if it
462             //     comes from the same wx app, then GTK+ hopefully won't
463             //     suggest something we didn't allow in the frist place
464             //     in DoDrop()
465             if ( (suggested_action == wxDragMove) && !(gs_flagsForDrag & wxDrag_AllowMove) )
466             {
467                 // we're requested to move but we can't
468                 suggested_action = wxDragCopy;
469             }
470 #endif
471         }
472     }
473     else if (GetDefaultAction() == wxDragMove &&
474             (actions & GDK_ACTION_MOVE))
475     {
476 
477         suggested_action = wxDragMove;
478     }
479     else
480     {
481         if (actions & GDK_ACTION_COPY)
482             suggested_action = wxDragCopy;
483         else if (actions & GDK_ACTION_MOVE)
484             suggested_action = wxDragMove;
485         else if (actions & GDK_ACTION_LINK)
486             suggested_action = wxDragLink;
487         else
488             suggested_action = wxDragNone;
489     }
490 
491     return suggested_action;
492 }
493 
GetMatchingPair()494 wxDataFormat wxDropTarget::GetMatchingPair()
495 {
496     return wxDataFormat( GTKGetMatchingPair() );
497 }
498 
GTKGetMatchingPair(bool quiet)499 GdkAtom wxDropTarget::GTKGetMatchingPair(bool quiet)
500 {
501     if (!m_dataObject)
502         return (GdkAtom) 0;
503 
504     if (!m_dragContext)
505         return (GdkAtom) 0;
506 
507     const GList* child = gdk_drag_context_list_targets(m_dragContext);
508     while (child)
509     {
510         GdkAtom formatAtom = (GdkAtom)(child->data);
511         wxDataFormat format( formatAtom );
512 
513         if ( !quiet )
514         {
515             wxLogTrace(TRACE_DND, wxT("Drop target: drag has format: %s"),
516                        format.GetId().c_str());
517         }
518 
519         if (m_dataObject->IsSupportedFormat( format ))
520             return formatAtom;
521 
522         child = child->next;
523     }
524 
525     return (GdkAtom) 0;
526 }
527 
GetData()528 bool wxDropTarget::GetData()
529 {
530     if (!m_dragData)
531         return false;
532 
533     if (!m_dataObject)
534         return false;
535 
536     wxDataFormat dragFormat(gtk_selection_data_get_target(m_dragData));
537 
538     if (!m_dataObject->IsSupportedFormat( dragFormat ))
539         return false;
540 
541     m_dataObject->SetData(dragFormat,
542         (size_t)gtk_selection_data_get_length(m_dragData),
543         (const void*)gtk_selection_data_get_data(m_dragData));
544 
545     return true;
546 }
547 
GtkUnregisterWidget(GtkWidget * widget)548 void wxDropTarget::GtkUnregisterWidget( GtkWidget *widget )
549 {
550     wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") );
551 
552     gtk_drag_dest_unset( widget );
553 
554     g_signal_handlers_disconnect_by_func (widget,
555                                           (gpointer) target_drag_leave, this);
556     g_signal_handlers_disconnect_by_func (widget,
557                                           (gpointer) target_drag_motion, this);
558     g_signal_handlers_disconnect_by_func (widget,
559                                           (gpointer) target_drag_drop, this);
560     g_signal_handlers_disconnect_by_func (widget,
561                                           (gpointer) target_drag_data_received, this);
562 }
563 
GtkRegisterWidget(GtkWidget * widget)564 void wxDropTarget::GtkRegisterWidget( GtkWidget *widget )
565 {
566     wxCHECK_RET( widget != NULL, wxT("register widget is NULL") );
567 
568     /* gtk_drag_dest_set() determines what default behaviour we'd like
569        GTK to supply. we don't want to specify out targets (=formats)
570        or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and
571        not GTK_DEST_DEFAULT_DROP). instead we react individually to
572        "drag_motion" and "drag_drop" events. this makes it possible
573        to allow dropping on only a small area. we should set
574        GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice
575        highlighting if dragging over standard controls, but this
576        seems to be broken without the other two. */
577 
578     gtk_drag_dest_set( widget,
579                        (GtkDestDefaults) 0,         /* no default behaviour */
580                        NULL,      /* we don't supply any formats here */
581                        0,                           /* number of targets = 0 */
582                        (GdkDragAction) 0 );         /* we don't supply any actions here */
583 
584     g_signal_connect (widget, "drag_leave",
585                       G_CALLBACK (target_drag_leave), this);
586 
587     g_signal_connect (widget, "drag_motion",
588                       G_CALLBACK (target_drag_motion), this);
589 
590     g_signal_connect (widget, "drag_drop",
591                       G_CALLBACK (target_drag_drop), this);
592 
593     g_signal_connect (widget, "drag_data_received",
594                       G_CALLBACK (target_drag_data_received), this);
595 }
596 
597 //----------------------------------------------------------------------------
598 // "drag_data_get"
599 //----------------------------------------------------------------------------
600 
601 extern "C" {
602 static void
source_drag_data_get(GtkWidget * WXUNUSED (widget),GdkDragContext * context,GtkSelectionData * selection_data,guint WXUNUSED (info),guint WXUNUSED (time),wxDropSource * drop_source)603 source_drag_data_get  (GtkWidget          *WXUNUSED(widget),
604                        GdkDragContext     *context,
605                        GtkSelectionData   *selection_data,
606                        guint               WXUNUSED(info),
607                        guint               WXUNUSED(time),
608                        wxDropSource       *drop_source )
609 {
610     wxDataFormat format(gtk_selection_data_get_target(selection_data));
611 
612     wxLogTrace(TRACE_DND, wxT("Drop source: format requested: %s"),
613                format.GetId().c_str());
614 
615     drop_source->m_retValue = wxDragError;
616 
617     wxDataObject *data = drop_source->GetDataObject();
618 
619     if (!data)
620     {
621         wxLogTrace(TRACE_DND, wxT("Drop source: no data object") );
622         return;
623     }
624 
625     if (!data->IsSupportedFormat(format))
626     {
627         wxLogTrace(TRACE_DND, wxT("Drop source: unsupported format") );
628         return;
629     }
630 
631     if (data->GetDataSize(format) == 0)
632     {
633         wxLogTrace(TRACE_DND, wxT("Drop source: empty data") );
634         return;
635     }
636 
637     size_t size = data->GetDataSize(format);
638 
639 //  printf( "data size: %d.\n", (int)data_size );
640 
641     guchar *d = new guchar[size];
642 
643     if (!data->GetDataHere( format, (void*)d ))
644     {
645         delete[] d;
646         return;
647     }
648 
649     drop_source->m_retValue = ConvertFromGTK(gdk_drag_context_get_selected_action(context));
650 
651     gtk_selection_data_set( selection_data,
652                             gtk_selection_data_get_target(selection_data),
653                             8,   // 8-bit
654                             d,
655                             size );
656 
657     delete[] d;
658 }
659 }
660 
661 //----------------------------------------------------------------------------
662 // "drag_end"
663 //----------------------------------------------------------------------------
664 
665 extern "C" {
source_drag_end(GtkWidget * WXUNUSED (widget),GdkDragContext * WXUNUSED (context),wxDropSource * drop_source)666 static void source_drag_end( GtkWidget          *WXUNUSED(widget),
667                              GdkDragContext     *WXUNUSED(context),
668                              wxDropSource       *drop_source )
669 {
670     drop_source->m_waiting = false;
671 }
672 }
673 
674 //-----------------------------------------------------------------------------
675 // "configure_event" from m_iconWindow
676 //-----------------------------------------------------------------------------
677 
678 extern "C" {
679 static gint
gtk_dnd_window_configure_callback(GtkWidget * WXUNUSED (widget),GdkEventConfigure * WXUNUSED (event),wxDropSource * source)680 gtk_dnd_window_configure_callback( GtkWidget *WXUNUSED(widget), GdkEventConfigure *WXUNUSED(event), wxDropSource *source )
681 {
682     source->GiveFeedback(ConvertFromGTK(gdk_drag_context_get_selected_action(source->m_dragContext)));
683 
684     return 0;
685 }
686 }
687 
688 //---------------------------------------------------------------------------
689 // wxDropSource
690 //---------------------------------------------------------------------------
691 
wxDropSource(wxWindow * win,const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)692 wxDropSource::wxDropSource(wxWindow *win,
693                            const wxIcon &iconCopy,
694                            const wxIcon &iconMove,
695                            const wxIcon &iconNone)
696 {
697     m_waiting = true;
698 
699     m_iconWindow = NULL;
700 
701     m_window = win;
702     m_widget = win->m_widget;
703     if (win->m_wxwindow) m_widget = win->m_wxwindow;
704 
705     m_retValue = wxDragNone;
706 
707     SetIcons(iconCopy, iconMove, iconNone);
708 }
709 
wxDropSource(wxDataObject & data,wxWindow * win,const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)710 wxDropSource::wxDropSource(wxDataObject& data,
711                            wxWindow *win,
712                            const wxIcon &iconCopy,
713                            const wxIcon &iconMove,
714                            const wxIcon &iconNone)
715 {
716     m_waiting = true;
717 
718     SetData( data );
719 
720     m_iconWindow = NULL;
721 
722     m_window = win;
723     m_widget = win->m_widget;
724     if (win->m_wxwindow) m_widget = win->m_wxwindow;
725 
726     m_retValue = wxDragNone;
727 
728     SetIcons(iconCopy, iconMove, iconNone);
729 }
730 
SetIcons(const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)731 void wxDropSource::SetIcons(const wxIcon &iconCopy,
732                             const wxIcon &iconMove,
733                             const wxIcon &iconNone)
734 {
735     m_iconCopy = iconCopy;
736     m_iconMove = iconMove;
737     m_iconNone = iconNone;
738 
739     if ( !m_iconCopy.IsOk() )
740         m_iconCopy = wxIcon(page_xpm);
741     if ( !m_iconMove.IsOk() )
742         m_iconMove = m_iconCopy;
743     if ( !m_iconNone.IsOk() )
744         m_iconNone = m_iconCopy;
745 }
746 
~wxDropSource()747 wxDropSource::~wxDropSource()
748 {
749 }
750 
PrepareIcon(int action,GdkDragContext * context)751 void wxDropSource::PrepareIcon( int action, GdkDragContext *context )
752 {
753     // get the right icon to display
754     wxIcon *icon = NULL;
755     if ( action & GDK_ACTION_MOVE )
756         icon = &m_iconMove;
757     else if ( action & GDK_ACTION_COPY )
758         icon = &m_iconCopy;
759     else
760         icon = &m_iconNone;
761 
762 #ifndef __WXGTK3__
763     GdkBitmap *mask;
764     if ( icon->GetMask() )
765         mask = *icon->GetMask();
766     else
767         mask = NULL;
768 
769     GdkPixmap *pixmap = icon->GetPixmap();
770 
771     GdkColormap *colormap = gtk_widget_get_colormap( m_widget );
772     gtk_widget_push_colormap (colormap);
773 #endif
774 
775     m_iconWindow = gtk_window_new (GTK_WINDOW_POPUP);
776     gtk_widget_set_events (m_iconWindow, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
777     gtk_widget_set_app_paintable (m_iconWindow, TRUE);
778 
779 #ifdef __WXGTK3__
780     gtk_widget_set_visual(m_iconWindow, gtk_widget_get_visual(m_widget));
781 #else
782     gtk_widget_pop_colormap ();
783 #endif
784 
785     gtk_widget_set_size_request (m_iconWindow, icon->GetWidth(), icon->GetHeight());
786     gtk_widget_realize (m_iconWindow);
787 
788     g_signal_connect (m_iconWindow, "configure_event",
789                       G_CALLBACK (gtk_dnd_window_configure_callback), this);
790 
791 #ifdef __WXGTK3__
792     cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(m_iconWindow));
793     icon->SetSourceSurface(cr, 0, 0);
794     cairo_pattern_t* pattern = cairo_get_source(cr);
795     gdk_window_set_background_pattern(gtk_widget_get_window(m_iconWindow), pattern);
796     cairo_destroy(cr);
797     cairo_surface_t* mask = NULL;
798     if (icon->GetMask())
799         mask = *icon->GetMask();
800     if (mask)
801     {
802         cairo_region_t* region = gdk_cairo_region_create_from_surface(mask);
803         gtk_widget_shape_combine_region(m_iconWindow, region);
804         cairo_region_destroy(region);
805     }
806 #else
807     gdk_window_set_back_pixmap(gtk_widget_get_window(m_iconWindow), pixmap, false);
808 
809     if (mask)
810         gtk_widget_shape_combine_mask (m_iconWindow, mask, 0, 0);
811 #endif
812 
813     gtk_drag_set_icon_widget( context, m_iconWindow, 0, 0 );
814 }
815 
DoDragDrop(int flags)816 wxDragResult wxDropSource::DoDragDrop(int flags)
817 {
818     wxCHECK_MSG( m_data && m_data->GetFormatCount(), wxDragNone,
819                  wxT("Drop source: no data") );
820 
821     // still in drag
822     if (g_blockEventsOnDrag)
823         return wxDragNone;
824 
825     // don't start dragging if no button is down
826     if (g_lastButtonNumber == 0)
827         return wxDragNone;
828 
829     // we can only start a drag after a mouse event
830     if (g_lastMouseEvent == NULL)
831         return wxDragNone;
832 
833     GTKConnectDragSignals();
834     wxON_BLOCK_EXIT_OBJ0(*this, wxDropSource::GTKDisconnectDragSignals);
835 
836     m_waiting = true;
837 
838     GtkTargetList *target_list = gtk_target_list_new( NULL, 0 );
839 
840     wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
841     m_data->GetAllFormats( array );
842     size_t count = m_data->GetFormatCount();
843     for (size_t i = 0; i < count; i++)
844     {
845         GdkAtom atom = array[i];
846         wxLogTrace(TRACE_DND, wxT("Drop source: Supported atom %s"),
847                    gdk_atom_name( atom ));
848         gtk_target_list_add( target_list, atom, 0, 0 );
849     }
850     delete[] array;
851 
852     int allowed_actions = GDK_ACTION_COPY;
853     if ( flags & wxDrag_AllowMove )
854         allowed_actions |= GDK_ACTION_MOVE;
855 
856     // VZ: as we already use g_blockEventsOnDrag it shouldn't be that bad
857     //     to use a global to pass the flags to the drop target but I'd
858     //     surely prefer a better way to do it
859     gs_flagsForDrag = flags;
860 
861     m_retValue = wxDragCancel;
862 
863     GdkDragContext *context = gtk_drag_begin( m_widget,
864                 target_list,
865                 (GdkDragAction)allowed_actions,
866                 g_lastButtonNumber,  // number of mouse button which started drag
867                 (GdkEvent*) g_lastMouseEvent );
868 
869     if ( !context )
870     {
871         // this can happen e.g. if gdk_pointer_grab() failed
872         return wxDragError;
873     }
874 
875     m_dragContext = context;
876 
877     PrepareIcon( allowed_actions, context );
878 
879     while (m_waiting)
880         gtk_main_iteration();
881 
882     g_signal_handlers_disconnect_by_func (m_iconWindow,
883                                           (gpointer) gtk_dnd_window_configure_callback, this);
884 
885     return m_retValue;
886 }
887 
GTKConnectDragSignals()888 void wxDropSource::GTKConnectDragSignals()
889 {
890     if (!m_widget)
891         return;
892 
893     g_blockEventsOnDrag = true;
894 
895     g_signal_connect (m_widget, "drag_data_get",
896                       G_CALLBACK (source_drag_data_get), this);
897     g_signal_connect (m_widget, "drag_end",
898                       G_CALLBACK (source_drag_end), this);
899 
900 }
901 
GTKDisconnectDragSignals()902 void wxDropSource::GTKDisconnectDragSignals()
903 {
904     if (!m_widget)
905         return;
906 
907     g_blockEventsOnDrag = false;
908 
909     g_signal_handlers_disconnect_by_func (m_widget,
910                                           (gpointer) source_drag_data_get,
911                                           this);
912     g_signal_handlers_disconnect_by_func (m_widget,
913                                           (gpointer) source_drag_end,
914                                           this);
915 }
916 
917 #endif
918       // wxUSE_DRAG_AND_DROP
919