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