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