More DnD.
[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 #ifdef __GNUG__
11 #pragma implementation "dnd.h"
12 #endif
13
14 #include "wx/dnd.h"
15
16 #if wxUSE_DRAG_AND_DROP
17
18 #include "wx/window.h"
19 #include "wx/app.h"
20 #include "wx/gdicmn.h"
21 #include "wx/intl.h"
22 #include "wx/utils.h"
23
24 #include "gdk/gdk.h"
25 #include "gtk/gtk.h"
26 #include "gdk/gdkprivate.h"
27
28 #include "gtk/gtkdnd.h"
29 #include "gtk/gtkselection.h"
30
31 //-----------------------------------------------------------------------------
32 // idle system
33 //-----------------------------------------------------------------------------
34
35 extern void wxapp_install_idle_handler();
36 extern bool g_isIdle;
37
38 //-----------------------------------------------------------------------------
39 // thread system
40 //-----------------------------------------------------------------------------
41
42 #if wxUSE_THREADS
43 extern void wxapp_install_thread_wakeup();
44 extern void wxapp_uninstall_thread_wakeup();
45 #endif
46
47 //----------------------------------------------------------------------------
48 // global data
49 //----------------------------------------------------------------------------
50
51 extern bool g_blockEventsOnDrag;
52
53 //----------------------------------------------------------------------------
54 // standard icons
55 //----------------------------------------------------------------------------
56
57 /* XPM */
58 static char * gv_xpm[] = {
59 "40 34 3 1",
60 " s None c None",
61 ". c black",
62 "X c white",
63 " ",
64 " ",
65 " ...... ",
66 " ..XXXXXX.. ",
67 " .XXXXXXXXXX. ",
68 " .XXXXXXXXXXXX. ",
69 " .XXXXXXXXXXXX. ",
70 " .XXXXXXXXXXXXXX. ",
71 " .XXX..XXXX..XXX. ",
72 " ....XX....XX....XX. ",
73 " .XXX.XXX..XXXX..XXX.... ",
74 " .XXXXXXXXXXXXXXXXXXX.XXX. ",
75 " .XXXXXXXXXXXXXXXXXXXXXXXX. ",
76 " .XXXXXXXXXXXXXXXXXXXXXXXX. ",
77 " ..XXXXXXXXXXXXXXXXXXXXXX. ",
78 " .XXXXXXXXXXXXXXXXXX... ",
79 " ..XXXXXXXXXXXXXXXX. ",
80 " .XXXXXXXXXXXXXXXX. ",
81 " .XXXXXXXXXXXXXXXX. ",
82 " .XXXXXXXXXXXXXXXXX. ",
83 " .XXXXXXXXXXXXXXXXX. ",
84 " .XXXXXXXXXXXXXXXXXX. ",
85 " .XXXXXXXXXXXXXXXXXXX. ",
86 " .XXXXXXXXXXXXXXXXXXXXX. ",
87 " .XXXXXXXXXXXXXX.XXXXXXX. ",
88 " .XXXXXXX.XXXXXXX.XXXXXXX. ",
89 " .XXXXXXXX.XXXXXXX.XXXXXXX. ",
90 " .XXXXXXX...XXXXX...XXXXX. ",
91 " .XXXXXXX. ..... ..... ",
92 " ..XXXX.. ",
93 " .... ",
94 " ",
95 " ",
96 " "};
97
98 /* XPM */
99 static char * page_xpm[] = {
100 /* width height ncolors chars_per_pixel */
101 "32 32 5 1",
102 /* colors */
103 " s None c None",
104 ". c black",
105 "X c wheat",
106 "o c tan",
107 "O c #6699FF",
108 /* pixels */
109 " ................... ",
110 " .XXXXXXXXXXXXXXXXX.. ",
111 " .XXXXXXXXXXXXXXXXX.o. ",
112 " .XXXXXXXXXXXXXXXXX.oo. ",
113 " .XXXXXXXXXXXXXXXXX.ooo. ",
114 " .XXXXXXXXXXXXXXXXX.oooo. ",
115 " .XXXXXXXXXXXXXXXXX....... ",
116 " .XXXXXOOOOOOOOOOXXXooooo. ",
117 " .XXXXXXXXXXXXXXXXXXooooo. ",
118 " .XXXXXOOOOOOOOOOXXXXXXXX. ",
119 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
120 " .XXXXXXXOOOOOOOOOXXXXXXX. ",
121 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
122 " .XXXXXXOOOOOOOOOOXXXXXXX. ",
123 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
124 " .XXXXXOOOOOOOOOOXXXXXXXX. ",
125 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
126 " .XXXXXXXOOOOOOOOOXXXXXXX. ",
127 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
128 " .XXXXXXOOOOOOOOOOXXXXXXX. ",
129 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
130 " .XXXXXOOOOOOOOOOXXXXXXXX. ",
131 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
132 " .XXXXXXOOOOOOOOOOXXXXXXX. ",
133 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
134 " .XXXXXOOOOOOOXXXXXXXXXXX. ",
135 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
136 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
137 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
138 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
139 " .XXXXXXXXXXXXXXXXXXXXXXX. ",
140 " ......................... "};
141
142
143
144 // ----------------------------------------------------------------------------
145 // "drag_leave"
146 // ----------------------------------------------------------------------------
147
148 static void target_drag_leave( GtkWidget *WXUNUSED(widget),
149 GdkDragContext *context,
150 guint WXUNUSED(time),
151 wxDropTarget *drop_target )
152 {
153 if (g_isIdle) wxapp_install_idle_handler();
154
155 /* inform the wxDropTarget about the current GdkDragContext.
156 this is only valid for the duration of this call */
157 drop_target->SetDragContext( context );
158
159 /* we don't need return values. this event is just for
160 information */
161 drop_target->OnLeave();
162
163 /* this has to be done because GDK has no "drag_enter" event */
164 drop_target->m_firstMotion = TRUE;
165
166 /* after this, invalidate the drop_target's GdkDragContext */
167 drop_target->SetDragContext( (GdkDragContext*) NULL );
168 }
169
170 // ----------------------------------------------------------------------------
171 // "drag_motion"
172 // ----------------------------------------------------------------------------
173
174 static gboolean target_drag_motion( GtkWidget *WXUNUSED(widget),
175 GdkDragContext *context,
176 gint x,
177 gint y,
178 guint time,
179 wxDropTarget *drop_target )
180 {
181 if (g_isIdle) wxapp_install_idle_handler();
182
183 /* Owen Taylor: "if the coordinates not in a drop zone,
184 return FALSE, otherwise call gtk_drag_status() and
185 return TRUE" */
186
187 /* inform the wxDropTarget about the current GdkDragContext.
188 this is only valid for the duration of this call */
189 drop_target->SetDragContext( context );
190
191 bool ret = FALSE;
192
193 if (drop_target->m_firstMotion)
194 {
195 /* the first "drag_motion" event substitutes a "drag_enter" event */
196 ret = drop_target->OnEnter( x, y );
197 }
198 else
199 {
200 /* give program a chance to react (i.e. to say no by returning FALSE) */
201 ret = drop_target->OnMove( x, y );
202 }
203
204 /* we don't yet handle which "actions" (i.e. copy or move)
205 the target accepts. so far we simply accept the
206 suggested action. TODO. */
207 if (ret)
208 gdk_drag_status( context, context->suggested_action, time );
209
210 /* after this, invalidate the drop_target's GdkDragContext */
211 drop_target->SetDragContext( (GdkDragContext*) NULL );
212
213 /* this has to be done because GDK has no "drag_enter" event */
214 drop_target->m_firstMotion = FALSE;
215
216 return ret;
217 }
218
219 // ----------------------------------------------------------------------------
220 // "drag_drop"
221 // ----------------------------------------------------------------------------
222
223 static gboolean target_drag_drop( GtkWidget *widget,
224 GdkDragContext *context,
225 gint x,
226 gint y,
227 guint time,
228 wxDropTarget *drop_target )
229 {
230 if (g_isIdle) wxapp_install_idle_handler();
231
232 /* Owen Taylor: "if the drop is not in a drop zone,
233 return FALSE, otherwise, if you aren't accepting
234 the drop, call gtk_drag_finish() with success == FALSE
235 otherwise call gtk_drag_data_get()" */
236
237 // printf( "drop.\n" );
238
239 /* this seems to make a difference between not accepting
240 due to wrong target area and due to wrong format. let
241 us hope that this is not required.. */
242
243 /* inform the wxDropTarget about the current GdkDragContext.
244 this is only valid for the duration of this call */
245 drop_target->SetDragContext( context );
246
247 /* inform the wxDropTarget about the current drag widget.
248 this is only valid for the duration of this call */
249 drop_target->SetDragWidget( widget );
250
251 /* inform the wxDropTarget about the current drag time.
252 this is only valid for the duration of this call */
253 drop_target->SetDragTime( time );
254
255 bool ret = drop_target->OnDrop( x, y );
256
257 if (!ret)
258 {
259 wxLogDebug( wxT( "Drop target: OnDrop returned TRUE") );
260
261 /* cancel the whole thing */
262 gtk_drag_finish( context,
263 FALSE, /* no success */
264 FALSE, /* don't delete data on dropping side */
265 time );
266 }
267 else
268 {
269 wxLogDebug( wxT( "Drop target: OnDrop returned TRUE") );
270
271 #if wxUSE_THREADS
272 /* disable GUI threads */
273 wxapp_uninstall_thread_wakeup();
274 #endif
275
276 GdkAtom format = drop_target->GetMatchingPair();
277 wxASSERT( format );
278
279 /* this should trigger an "drag_data_received" event */
280 gtk_drag_get_data( widget,
281 context,
282 format,
283 time );
284
285 #if wxUSE_THREADS
286 /* re-enable GUI threads */
287 wxapp_install_thread_wakeup();
288 #endif
289 }
290
291 /* after this, invalidate the drop_target's GdkDragContext */
292 drop_target->SetDragContext( (GdkDragContext*) NULL );
293
294 /* after this, invalidate the drop_target's drag widget */
295 drop_target->SetDragWidget( (GtkWidget*) NULL );
296
297 /* this has to be done because GDK has no "drag_enter" event */
298 drop_target->m_firstMotion = TRUE;
299
300 return ret;
301 }
302
303 // ----------------------------------------------------------------------------
304 // "drag_data_received"
305 // ----------------------------------------------------------------------------
306
307 static void target_drag_data_received( GtkWidget *WXUNUSED(widget),
308 GdkDragContext *context,
309 gint x,
310 gint y,
311 GtkSelectionData *data,
312 guint WXUNUSED(info),
313 guint time,
314 wxDropTarget *drop_target )
315 {
316 if (g_isIdle) wxapp_install_idle_handler();
317
318 /* Owen Taylor: "call gtk_drag_finish() with
319 success == TRUE" */
320
321
322 if ((data->length <= 0) || (data->format != 8))
323 {
324 /* negative data length and non 8-bit data format
325 qualifies for junk */
326 gtk_drag_finish (context, FALSE, FALSE, time);
327
328 return;
329 }
330
331 wxLogDebug( wxT( "Drop target: data received event") );
332
333 /* inform the wxDropTarget about the current GtkSelectionData.
334 this is only valid for the duration of this call */
335 drop_target->SetDragData( data );
336
337 if (drop_target->OnData( x, y ))
338 {
339 wxLogDebug( wxT( "Drop target: OnData returned TRUE") );
340
341 /* tell GTK that data transfer was successfull */
342 gtk_drag_finish( context, TRUE, FALSE, time );
343 }
344 else
345 {
346 wxLogDebug( wxT( "Drop target: OnData returned FALSE") );
347
348 /* tell GTK that data transfer was not successfull */
349 gtk_drag_finish( context, FALSE, FALSE, time );
350 }
351
352 /* after this, invalidate the drop_target's drag data */
353 drop_target->SetDragData( (GtkSelectionData*) NULL );
354 }
355
356 //----------------------------------------------------------------------------
357 // wxDropTarget
358 //----------------------------------------------------------------------------
359
360 wxDropTarget::wxDropTarget( wxDataObject *data )
361 : wxDropTargetBase( data )
362 {
363 m_firstMotion = TRUE;
364 m_dragContext = (GdkDragContext*) NULL;
365 m_dragWidget = (GtkWidget*) NULL;
366 m_dragData = (GtkSelectionData*) NULL;
367 m_dragTime = 0;
368 }
369
370 bool wxDropTarget::OnEnter( int WXUNUSED(x), int WXUNUSED(y) )
371 {
372 if (!m_dataObject)
373 return FALSE;
374
375 return (GetMatchingPair() != (GdkAtom) 0);
376 }
377
378 bool wxDropTarget::OnMove( int WXUNUSED(x), int WXUNUSED(y) )
379 {
380 if (!m_dataObject)
381 return FALSE;
382
383 return (GetMatchingPair() != (GdkAtom) 0);
384 }
385
386 bool wxDropTarget::OnDrop( int WXUNUSED(x), int WXUNUSED(y) )
387 {
388 if (!m_dataObject)
389 return FALSE;
390
391 return (GetMatchingPair() != (GdkAtom) 0);
392 }
393
394 bool wxDropTarget::OnData( int WXUNUSED(x), int WXUNUSED(y) )
395 {
396 if (!m_dataObject)
397 return FALSE;
398
399 if (GetMatchingPair() == (GdkAtom) 0)
400 return FALSE;
401
402 return GetData();
403 }
404
405 GdkAtom wxDropTarget::GetMatchingPair()
406 {
407 if (!m_dataObject)
408 return (GdkAtom) 0;
409
410 if (!m_dragContext)
411 return (GdkAtom) 0;
412
413 GList *child = m_dragContext->targets;
414 while (child)
415 {
416 GdkAtom formatAtom = (GdkAtom) GPOINTER_TO_INT(child->data);
417 wxDataFormat format( formatAtom );
418
419 #ifdef __WXDEBUG__
420 char *name = gdk_atom_name( formatAtom );
421 if (name) wxLogDebug( "Drop target: drag has format: %s", name );
422 #endif
423 if (m_dataObject->IsSupportedFormat( format ))
424 return formatAtom;
425
426 child = child->next;
427 }
428
429 return (GdkAtom) 0;
430 }
431
432 bool wxDropTarget::GetData()
433 {
434 if (!m_dragData)
435 return FALSE;
436
437 if (!m_dataObject)
438 return FALSE;
439
440 wxDataFormat dragFormat( m_dragData->target );
441
442 if (!m_dataObject->IsSupportedFormat( dragFormat ))
443 return FALSE;
444
445 if (dragFormat.GetType() == wxDF_TEXT)
446 {
447 wxTextDataObject *text_object = (wxTextDataObject*)m_dataObject;
448 text_object->SetText( (const char*)m_dragData->data );
449 return TRUE;
450 }
451
452 if (dragFormat.GetType() == wxDF_FILENAME)
453 {
454 wxFileDataObject *file_object = (wxFileDataObject*)m_dataObject;
455 file_object->SetData( 0, (const char*)m_dragData->data );
456 return TRUE;
457 }
458
459 m_dataObject->SetData( dragFormat, (size_t)m_dragData->length, (const void*)m_dragData->data );
460
461 return TRUE;
462 }
463
464 void wxDropTarget::UnregisterWidget( GtkWidget *widget )
465 {
466 wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") );
467
468 gtk_drag_dest_unset( widget );
469
470 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
471 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
472
473 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
474 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
475
476 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
477 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
478
479 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
480 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
481 }
482
483 void wxDropTarget::RegisterWidget( GtkWidget *widget )
484 {
485 wxCHECK_RET( widget != NULL, wxT("register widget is NULL") );
486
487 /* gtk_drag_dest_set() determines what default behaviour we'd like
488 GTK to supply. we don't want to specify out targets (=formats)
489 or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and
490 not GTK_DEST_DEFAULT_DROP). instead we react individually to
491 "drag_motion" and "drag_drop" events. this makes it possible
492 to allow dropping on only a small area. we should set
493 GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice
494 highlighting if dragging over standard controls, but this
495 seems to be broken without the other two. */
496
497 gtk_drag_dest_set( widget,
498 (GtkDestDefaults) 0, /* no default behaviour */
499 (GtkTargetEntry*) NULL, /* we don't supply any formats here */
500 0, /* number of targets = 0 */
501 (GdkDragAction) 0 ); /* we don't supply any actions here */
502
503 gtk_signal_connect( GTK_OBJECT(widget), "drag_leave",
504 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
505
506 gtk_signal_connect( GTK_OBJECT(widget), "drag_motion",
507 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
508
509 gtk_signal_connect( GTK_OBJECT(widget), "drag_drop",
510 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
511
512 gtk_signal_connect( GTK_OBJECT(widget), "drag_data_received",
513 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
514 }
515
516 // ----------------------------------------------------------------------------
517 // wxTextDropTarget
518 // ----------------------------------------------------------------------------
519
520 wxTextDropTarget::wxTextDropTarget()
521 : wxDropTarget(new wxTextDataObject)
522 {
523 }
524
525 bool wxTextDropTarget::OnData(wxCoord x, wxCoord y)
526 {
527 if ( !GetData() )
528 return FALSE;
529
530 return OnDropText(x, y, ((wxTextDataObject *)m_dataObject)->GetText());
531 }
532
533 // ----------------------------------------------------------------------------
534 // wxFileDropTarget
535 // ----------------------------------------------------------------------------
536
537 wxFileDropTarget::wxFileDropTarget()
538 : wxDropTarget(new wxFileDataObject)
539 {
540 }
541
542 bool wxFileDropTarget::OnData(wxCoord x, wxCoord y)
543 {
544 if ( !GetData() )
545 return FALSE;
546
547 return OnDropFiles(x, y,
548 ((wxFileDataObject *)m_dataObject)->GetFilenames());
549 }
550
551 //----------------------------------------------------------------------------
552 // "drag_data_get"
553 //----------------------------------------------------------------------------
554
555 static void
556 source_drag_data_get (GtkWidget *WXUNUSED(widget),
557 GdkDragContext *context,
558 GtkSelectionData *selection_data,
559 guint WXUNUSED(info),
560 guint WXUNUSED(time),
561 wxDropSource *drop_source )
562 {
563 if (g_isIdle) wxapp_install_idle_handler();
564
565 wxDataFormat format( selection_data->target );
566
567 wxLogDebug( wxT("Drop source: format requested: %s"), format.GetId().c_str() );
568
569 drop_source->m_retValue = wxDragCancel;
570
571 wxDataObject *data = drop_source->GetDataObject();
572
573 if (!data)
574 {
575 wxLogDebug( wxT("Drop source: no data object") );
576 return;
577 }
578
579 if (!data->IsSupportedFormat(format))
580 {
581 wxLogDebug( wxT("Drop source: unsupported format") );
582 return;
583 }
584
585 if (data->GetDataSize(format) == 0)
586 {
587 wxLogDebug( wxT("Drop source: empty data") );
588 return;
589 }
590
591 size_t size = data->GetDataSize(format);
592
593 // printf( "data size: %d.\n", (int)data_size );
594
595 guchar *d = new guchar[size];
596
597 if (!data->GetDataHere( format, (void*)d ))
598 {
599 delete[] d;
600 return;
601 }
602
603 #if wxUSE_THREADS
604 /* disable GUI threads */
605 wxapp_uninstall_thread_wakeup();
606 #endif
607
608 gtk_selection_data_set( selection_data,
609 selection_data->target,
610 8, // 8-bit
611 d,
612 size );
613
614 #if wxUSE_THREADS
615 /* enable GUI threads */
616 wxapp_install_thread_wakeup();
617 #endif
618
619 delete[] d;
620
621 /* so far only copy, no moves. TODO. */
622 drop_source->m_retValue = wxDragCopy;
623 }
624
625 //----------------------------------------------------------------------------
626 // "drag_data_delete"
627 //----------------------------------------------------------------------------
628
629 static void source_drag_data_delete( GtkWidget *WXUNUSED(widget),
630 GdkDragContext *WXUNUSED(context),
631 wxDropSource *drop_source )
632 {
633 if (g_isIdle) wxapp_install_idle_handler();
634
635 // printf( "Delete the data!\n" );
636
637 drop_source->m_retValue = wxDragMove;
638 }
639
640 //----------------------------------------------------------------------------
641 // "drag_begin"
642 //----------------------------------------------------------------------------
643
644 static void source_drag_begin( GtkWidget *WXUNUSED(widget),
645 GdkDragContext *WXUNUSED(context),
646 wxDropSource *WXUNUSED(drop_source) )
647 {
648 if (g_isIdle) wxapp_install_idle_handler();
649
650 // printf( "drag_begin.\n" );
651 }
652
653 //----------------------------------------------------------------------------
654 // "drag_end"
655 //----------------------------------------------------------------------------
656
657 static void source_drag_end( GtkWidget *WXUNUSED(widget),
658 GdkDragContext *WXUNUSED(context),
659 wxDropSource *drop_source )
660 {
661 if (g_isIdle) wxapp_install_idle_handler();
662
663 // printf( "drag_end.\n" );
664
665 drop_source->m_waiting = FALSE;
666 }
667
668 //---------------------------------------------------------------------------
669 // wxDropSource
670 //---------------------------------------------------------------------------
671
672 wxDropSource::wxDropSource( wxWindow *win, const wxIcon &go, const wxIcon &stop )
673 {
674 g_blockEventsOnDrag = TRUE;
675 m_waiting = TRUE;
676
677 m_window = win;
678 m_widget = win->m_widget;
679 if (win->m_wxwindow) m_widget = win->m_wxwindow;
680
681 m_retValue = wxDragCancel;
682
683 m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY );
684 m_goaheadCursor = wxCursor( wxCURSOR_HAND );
685
686 m_goIcon = go;
687 if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm );
688 m_stopIcon = stop;
689 if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm );
690 }
691
692 wxDropSource::wxDropSource( wxDataObject& data, wxWindow *win,
693 const wxIcon &go, const wxIcon &stop )
694 {
695 m_waiting = TRUE;
696
697 SetData( data );
698
699 m_window = win;
700 m_widget = win->m_widget;
701 if (win->m_wxwindow) m_widget = win->m_wxwindow;
702 m_retValue = wxDragCancel;
703
704 m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY );
705 m_goaheadCursor = wxCursor( wxCURSOR_HAND );
706
707 m_goIcon = go;
708 if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm );
709 m_stopIcon = stop;
710 if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm );
711 }
712
713 wxDropSource::~wxDropSource()
714 {
715 g_blockEventsOnDrag = FALSE;
716 }
717
718 wxDragResult wxDropSource::DoDragDrop( bool WXUNUSED(bAllowMove) )
719 {
720 wxASSERT_MSG( m_data, wxT("wxDragSource: no data") );
721
722 if (!m_data)
723 return (wxDragResult) wxDragNone;
724
725 if (m_data->GetFormatCount() == 0)
726 return (wxDragResult) wxDragNone;
727
728 g_blockEventsOnDrag = TRUE;
729
730 RegisterWindow();
731
732 m_waiting = TRUE;
733
734 GtkTargetList *target_list = gtk_target_list_new( (GtkTargetEntry*) NULL, 0 );
735
736 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
737 m_data->GetAllFormats( array );
738 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
739 {
740 GdkAtom atom = array[i];
741 wxLogDebug( wxT("Supported atom %s"), gdk_atom_name( atom ) );
742 gtk_target_list_add( target_list, atom, 0, 0 );
743 }
744 delete[] array;
745
746 GdkEventMotion event;
747 event.window = m_widget->window;
748 int x = 0;
749 int y = 0;
750 GdkModifierType state;
751 gdk_window_get_pointer( event.window, &x, &y, &state );
752 event.x = x;
753 event.y = y;
754 event.state = state;
755 event.time = GDK_CURRENT_TIME;
756
757 /* GTK wants to know which button was pressed which caused the dragging */
758 int button_number = 0;
759 if (event.state & GDK_BUTTON1_MASK) button_number = 1;
760 else if (event.state & GDK_BUTTON2_MASK) button_number = 2;
761 else if (event.state & GDK_BUTTON3_MASK) button_number = 3;
762
763 #if wxUSE_THREADS
764 /* disable GUI threads */
765 wxapp_uninstall_thread_wakeup();
766 #endif
767
768 /* don't start dragging if no button is down */
769 if (button_number)
770 {
771 GdkDragContext *context = gtk_drag_begin( m_widget,
772 target_list,
773 GDK_ACTION_COPY,
774 button_number, /* number of mouse button which started drag */
775 (GdkEvent*) &event );
776
777 wxMask *mask = m_goIcon.GetMask();
778 GdkBitmap *bm = (GdkBitmap *) NULL;
779 if (mask) bm = mask->GetBitmap();
780 GdkPixmap *pm = m_goIcon.GetPixmap();
781
782 gtk_drag_set_icon_pixmap( context,
783 gtk_widget_get_colormap( m_widget ),
784 pm,
785 bm,
786 0,
787 0 );
788
789 while (m_waiting) gtk_main_iteration();;
790 }
791
792 #if wxUSE_THREADS
793 /* re-enable GUI threads */
794 wxapp_install_thread_wakeup();
795 #endif
796
797 g_blockEventsOnDrag = FALSE;
798
799 UnregisterWindow();
800
801 return m_retValue;
802 }
803
804 void wxDropSource::RegisterWindow()
805 {
806 if (!m_widget) return;
807
808 gtk_signal_connect( GTK_OBJECT(m_widget), "drag_data_get",
809 GTK_SIGNAL_FUNC (source_drag_data_get), (gpointer) this);
810 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_data_delete",
811 GTK_SIGNAL_FUNC (source_drag_data_delete), (gpointer) this );
812 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_begin",
813 GTK_SIGNAL_FUNC (source_drag_begin), (gpointer) this );
814 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_end",
815 GTK_SIGNAL_FUNC (source_drag_end), (gpointer) this );
816
817 }
818
819 void wxDropSource::UnregisterWindow()
820 {
821 if (!m_widget) return;
822
823 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
824 GTK_SIGNAL_FUNC(source_drag_data_get), (gpointer) this );
825 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
826 GTK_SIGNAL_FUNC(source_drag_data_delete), (gpointer) this );
827 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
828 GTK_SIGNAL_FUNC(source_drag_begin), (gpointer) this );
829 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
830 GTK_SIGNAL_FUNC(source_drag_end), (gpointer) this );
831 }
832
833 #endif
834
835 // wxUSE_DRAG_AND_DROP