Compile fix for wxDataFormat cast,
[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 wxDragResult result = wxDragMove;
192 if (context->suggested_action == GDK_ACTION_COPY) result = wxDragCopy;
193
194 if (drop_target->m_firstMotion)
195 {
196 /* the first "drag_motion" event substitutes a "drag_enter" event */
197 result = drop_target->OnEnter( x, y, result );
198 }
199 else
200 {
201 /* give program a chance to react (i.e. to say no by returning FALSE) */
202 result = drop_target->OnDragOver( x, y, result );
203 }
204
205 bool ret = result != wxDragNone;
206 if (ret)
207 {
208 GdkDragAction action = GDK_ACTION_MOVE;
209 if (result == wxDragCopy) action == GDK_ACTION_COPY;
210 gdk_drag_status( context, context->suggested_action, time );
211 }
212
213 /* after this, invalidate the drop_target's GdkDragContext */
214 drop_target->SetDragContext( (GdkDragContext*) NULL );
215
216 /* this has to be done because GDK has no "drag_enter" event */
217 drop_target->m_firstMotion = FALSE;
218
219 return ret;
220 }
221
222 // ----------------------------------------------------------------------------
223 // "drag_drop"
224 // ----------------------------------------------------------------------------
225
226 static gboolean target_drag_drop( GtkWidget *widget,
227 GdkDragContext *context,
228 gint x,
229 gint y,
230 guint time,
231 wxDropTarget *drop_target )
232 {
233 if (g_isIdle) wxapp_install_idle_handler();
234
235 /* Owen Taylor: "if the drop is not in a drop zone,
236 return FALSE, otherwise, if you aren't accepting
237 the drop, call gtk_drag_finish() with success == FALSE
238 otherwise call gtk_drag_data_get()" */
239
240 // printf( "drop.\n" );
241
242 /* this seems to make a difference between not accepting
243 due to wrong target area and due to wrong format. let
244 us hope that this is not required.. */
245
246 /* inform the wxDropTarget about the current GdkDragContext.
247 this is only valid for the duration of this call */
248 drop_target->SetDragContext( context );
249
250 /* inform the wxDropTarget about the current drag widget.
251 this is only valid for the duration of this call */
252 drop_target->SetDragWidget( widget );
253
254 /* inform the wxDropTarget about the current drag time.
255 this is only valid for the duration of this call */
256 drop_target->SetDragTime( time );
257
258 /*
259 wxDragResult result = wxDragMove;
260 if (context->suggested_action == GDK_ACTION_COPY) result = wxDragCopy;
261 */
262
263 bool ret = drop_target->OnDrop( x, y );
264
265 if (!ret)
266 {
267 wxLogDebug( wxT( "Drop target: OnDrop returned TRUE") );
268
269 /* cancel the whole thing */
270 gtk_drag_finish( context,
271 FALSE, /* no success */
272 FALSE, /* don't delete data on dropping side */
273 time );
274 }
275 else
276 {
277 wxLogDebug( wxT( "Drop target: OnDrop returned TRUE") );
278
279 #if wxUSE_THREADS
280 /* disable GUI threads */
281 wxapp_uninstall_thread_wakeup();
282 #endif
283
284 GdkAtom format = drop_target->GetMatchingPair();
285 wxASSERT( format );
286
287 /*
288 GdkDragAction action = GDK_ACTION_MOVE;
289 if (result == wxDragCopy) action == GDK_ACTION_COPY;
290 context->action = action;
291 */
292 /* this should trigger an "drag_data_received" event */
293 gtk_drag_get_data( widget,
294 context,
295 format,
296 time );
297
298 #if wxUSE_THREADS
299 /* re-enable GUI threads */
300 wxapp_install_thread_wakeup();
301 #endif
302 }
303
304 /* after this, invalidate the drop_target's GdkDragContext */
305 drop_target->SetDragContext( (GdkDragContext*) NULL );
306
307 /* after this, invalidate the drop_target's drag widget */
308 drop_target->SetDragWidget( (GtkWidget*) NULL );
309
310 /* this has to be done because GDK has no "drag_enter" event */
311 drop_target->m_firstMotion = TRUE;
312
313 return ret;
314 }
315
316 // ----------------------------------------------------------------------------
317 // "drag_data_received"
318 // ----------------------------------------------------------------------------
319
320 static void target_drag_data_received( GtkWidget *WXUNUSED(widget),
321 GdkDragContext *context,
322 gint x,
323 gint y,
324 GtkSelectionData *data,
325 guint WXUNUSED(info),
326 guint time,
327 wxDropTarget *drop_target )
328 {
329 if (g_isIdle) wxapp_install_idle_handler();
330
331 /* Owen Taylor: "call gtk_drag_finish() with
332 success == TRUE" */
333
334
335 if ((data->length <= 0) || (data->format != 8))
336 {
337 /* negative data length and non 8-bit data format
338 qualifies for junk */
339 gtk_drag_finish (context, FALSE, FALSE, time);
340
341 return;
342 }
343
344 wxLogDebug( wxT( "Drop target: data received event") );
345
346 /* inform the wxDropTarget about the current GtkSelectionData.
347 this is only valid for the duration of this call */
348 drop_target->SetDragData( data );
349
350 if (drop_target->OnData( x, y ))
351 {
352 wxLogDebug( wxT( "Drop target: OnData returned TRUE") );
353
354 /* tell GTK that data transfer was successfull */
355 gtk_drag_finish( context, TRUE, FALSE, time );
356 }
357 else
358 {
359 wxLogDebug( wxT( "Drop target: OnData returned FALSE") );
360
361 /* tell GTK that data transfer was not successfull */
362 gtk_drag_finish( context, FALSE, FALSE, time );
363 }
364
365 /* after this, invalidate the drop_target's drag data */
366 drop_target->SetDragData( (GtkSelectionData*) NULL );
367 }
368
369 //----------------------------------------------------------------------------
370 // wxDropTarget
371 //----------------------------------------------------------------------------
372
373 wxDropTarget::wxDropTarget( wxDataObject *data )
374 : wxDropTargetBase( data )
375 {
376 m_firstMotion = TRUE;
377 m_dragContext = (GdkDragContext*) NULL;
378 m_dragWidget = (GtkWidget*) NULL;
379 m_dragData = (GtkSelectionData*) NULL;
380 m_dragTime = 0;
381 }
382
383 wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
384 wxCoord WXUNUSED(y),
385 wxDragResult def )
386 {
387 // GetMatchingPair() checks for m_dataObject too, no need to do it here
388
389 // disable the debug message from GetMatchingPair() - there are too many
390 // of them otherwise
391 #ifdef __WXDEBUG__
392 wxLogNull noLog;
393 #endif // Debug
394
395 return (GetMatchingPair() != (GdkAtom) 0) ? def : wxDragNone;
396 }
397
398 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
399 {
400 if (!m_dataObject)
401 return FALSE;
402
403 return (GetMatchingPair() != (GdkAtom) 0);
404 }
405
406 bool wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
407 {
408 if (!m_dataObject)
409 return FALSE;
410
411 if (GetMatchingPair() == (GdkAtom) 0)
412 return FALSE;
413
414 return GetData();
415 }
416
417 GdkAtom wxDropTarget::GetMatchingPair()
418 {
419 if (!m_dataObject)
420 return (GdkAtom) 0;
421
422 if (!m_dragContext)
423 return (GdkAtom) 0;
424
425 GList *child = m_dragContext->targets;
426 while (child)
427 {
428 GdkAtom formatAtom = (GdkAtom) GPOINTER_TO_INT(child->data);
429 wxDataFormat format( formatAtom );
430
431 #ifdef __WXDEBUG__
432 char *name = gdk_atom_name( formatAtom );
433 wxLogDebug("Drop target: drag has format: %s", name ? name : "unnamed");
434 #endif // Debug
435
436 if (m_dataObject->IsSupportedFormat( format ))
437 return formatAtom;
438
439 child = child->next;
440 }
441
442 return (GdkAtom) 0;
443 }
444
445 bool wxDropTarget::GetData()
446 {
447 if (!m_dragData)
448 return FALSE;
449
450 if (!m_dataObject)
451 return FALSE;
452
453 wxDataFormat dragFormat( m_dragData->target );
454
455 if (!m_dataObject->IsSupportedFormat( dragFormat ))
456 return FALSE;
457
458 if (dragFormat.GetType() == wxDF_TEXT)
459 {
460 wxTextDataObject *text_object = (wxTextDataObject*)m_dataObject;
461 text_object->SetText( (const char*)m_dragData->data );
462 return TRUE;
463 }
464
465 if (dragFormat.GetType() == wxDF_FILENAME)
466 {
467 wxFileDataObject *file_object = (wxFileDataObject*)m_dataObject;
468 file_object->SetData( 0, (const char*)m_dragData->data );
469 return TRUE;
470 }
471
472 m_dataObject->SetData( dragFormat, (size_t)m_dragData->length, (const void*)m_dragData->data );
473
474 return TRUE;
475 }
476
477 void wxDropTarget::UnregisterWidget( GtkWidget *widget )
478 {
479 wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") );
480
481 gtk_drag_dest_unset( widget );
482
483 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
484 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
485
486 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
487 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
488
489 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
490 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
491
492 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
493 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
494 }
495
496 void wxDropTarget::RegisterWidget( GtkWidget *widget )
497 {
498 wxCHECK_RET( widget != NULL, wxT("register widget is NULL") );
499
500 /* gtk_drag_dest_set() determines what default behaviour we'd like
501 GTK to supply. we don't want to specify out targets (=formats)
502 or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and
503 not GTK_DEST_DEFAULT_DROP). instead we react individually to
504 "drag_motion" and "drag_drop" events. this makes it possible
505 to allow dropping on only a small area. we should set
506 GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice
507 highlighting if dragging over standard controls, but this
508 seems to be broken without the other two. */
509
510 gtk_drag_dest_set( widget,
511 (GtkDestDefaults) 0, /* no default behaviour */
512 (GtkTargetEntry*) NULL, /* we don't supply any formats here */
513 0, /* number of targets = 0 */
514 (GdkDragAction) 0 ); /* we don't supply any actions here */
515
516 gtk_signal_connect( GTK_OBJECT(widget), "drag_leave",
517 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
518
519 gtk_signal_connect( GTK_OBJECT(widget), "drag_motion",
520 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
521
522 gtk_signal_connect( GTK_OBJECT(widget), "drag_drop",
523 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
524
525 gtk_signal_connect( GTK_OBJECT(widget), "drag_data_received",
526 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
527 }
528
529 // ----------------------------------------------------------------------------
530 // wxTextDropTarget
531 // ----------------------------------------------------------------------------
532
533 wxTextDropTarget::wxTextDropTarget()
534 : wxDropTarget(new wxTextDataObject)
535 {
536 }
537
538 bool wxTextDropTarget::OnData(wxCoord x, wxCoord y)
539 {
540 if ( !GetData() )
541 return FALSE;
542
543 return OnDropText(x, y, ((wxTextDataObject *)m_dataObject)->GetText());
544 }
545
546 // ----------------------------------------------------------------------------
547 // wxFileDropTarget
548 // ----------------------------------------------------------------------------
549
550 wxFileDropTarget::wxFileDropTarget()
551 : wxDropTarget(new wxFileDataObject)
552 {
553 }
554
555 bool wxFileDropTarget::OnData(wxCoord x, wxCoord y)
556 {
557 if ( !GetData() )
558 return FALSE;
559
560 return OnDropFiles(x, y,
561 ((wxFileDataObject *)m_dataObject)->GetFilenames());
562 }
563
564 //----------------------------------------------------------------------------
565 // "drag_data_get"
566 //----------------------------------------------------------------------------
567
568 static void
569 source_drag_data_get (GtkWidget *WXUNUSED(widget),
570 GdkDragContext *context,
571 GtkSelectionData *selection_data,
572 guint WXUNUSED(info),
573 guint WXUNUSED(time),
574 wxDropSource *drop_source )
575 {
576 if (g_isIdle) wxapp_install_idle_handler();
577
578 wxDataFormat format( selection_data->target );
579
580 wxLogDebug( wxT("Drop source: format requested: %s"), format.GetId().c_str() );
581
582 drop_source->m_retValue = wxDragCancel;
583
584 wxDataObject *data = drop_source->GetDataObject();
585
586 if (!data)
587 {
588 wxLogDebug( wxT("Drop source: no data object") );
589 return;
590 }
591
592 if (!data->IsSupportedFormat(format))
593 {
594 wxLogDebug( wxT("Drop source: unsupported format") );
595 return;
596 }
597
598 if (data->GetDataSize(format) == 0)
599 {
600 wxLogDebug( wxT("Drop source: empty data") );
601 return;
602 }
603
604 size_t size = data->GetDataSize(format);
605
606 // printf( "data size: %d.\n", (int)data_size );
607
608 guchar *d = new guchar[size];
609
610 if (!data->GetDataHere( format, (void*)d ))
611 {
612 delete[] d;
613 return;
614 }
615
616 #if wxUSE_THREADS
617 /* disable GUI threads */
618 wxapp_uninstall_thread_wakeup();
619 #endif
620
621 gtk_selection_data_set( selection_data,
622 selection_data->target,
623 8, // 8-bit
624 d,
625 size );
626
627 #if wxUSE_THREADS
628 /* enable GUI threads */
629 wxapp_install_thread_wakeup();
630 #endif
631
632 delete[] d;
633
634 /* so far only copy, no moves. TODO. */
635 drop_source->m_retValue = wxDragCopy;
636 }
637
638 //----------------------------------------------------------------------------
639 // "drag_data_delete"
640 //----------------------------------------------------------------------------
641
642 static void source_drag_data_delete( GtkWidget *WXUNUSED(widget),
643 GdkDragContext *WXUNUSED(context),
644 wxDropSource *drop_source )
645 {
646 if (g_isIdle) wxapp_install_idle_handler();
647
648 // printf( "Delete the data!\n" );
649
650 drop_source->m_retValue = wxDragMove;
651 }
652
653 //----------------------------------------------------------------------------
654 // "drag_begin"
655 //----------------------------------------------------------------------------
656
657 static void source_drag_begin( GtkWidget *WXUNUSED(widget),
658 GdkDragContext *WXUNUSED(context),
659 wxDropSource *WXUNUSED(drop_source) )
660 {
661 if (g_isIdle) wxapp_install_idle_handler();
662
663 // printf( "drag_begin.\n" );
664 }
665
666 //----------------------------------------------------------------------------
667 // "drag_end"
668 //----------------------------------------------------------------------------
669
670 static void source_drag_end( GtkWidget *WXUNUSED(widget),
671 GdkDragContext *WXUNUSED(context),
672 wxDropSource *drop_source )
673 {
674 if (g_isIdle) wxapp_install_idle_handler();
675
676 // printf( "drag_end.\n" );
677
678 drop_source->m_waiting = FALSE;
679 }
680
681 //---------------------------------------------------------------------------
682 // wxDropSource
683 //---------------------------------------------------------------------------
684
685 wxDropSource::wxDropSource( wxWindow *win, const wxIcon &go, const wxIcon &stop )
686 {
687 g_blockEventsOnDrag = TRUE;
688 m_waiting = TRUE;
689
690 m_window = win;
691 m_widget = win->m_widget;
692 if (win->m_wxwindow) m_widget = win->m_wxwindow;
693
694 m_retValue = wxDragCancel;
695
696 m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY );
697 m_goaheadCursor = wxCursor( wxCURSOR_HAND );
698
699 m_goIcon = go;
700 if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm );
701 m_stopIcon = stop;
702 if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm );
703 }
704
705 wxDropSource::wxDropSource( wxDataObject& data, wxWindow *win,
706 const wxIcon &go, const wxIcon &stop )
707 {
708 m_waiting = TRUE;
709
710 SetData( data );
711
712 m_window = win;
713 m_widget = win->m_widget;
714 if (win->m_wxwindow) m_widget = win->m_wxwindow;
715 m_retValue = wxDragCancel;
716
717 m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY );
718 m_goaheadCursor = wxCursor( wxCURSOR_HAND );
719
720 m_goIcon = go;
721 if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm );
722 m_stopIcon = stop;
723 if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm );
724 }
725
726 wxDropSource::~wxDropSource()
727 {
728 g_blockEventsOnDrag = FALSE;
729 }
730
731 wxDragResult wxDropSource::DoDragDrop( bool WXUNUSED(bAllowMove) )
732 {
733 wxASSERT_MSG( m_data, wxT("wxDragSource: no data") );
734
735 if (!m_data)
736 return (wxDragResult) wxDragNone;
737
738 if (m_data->GetFormatCount() == 0)
739 return (wxDragResult) wxDragNone;
740
741 g_blockEventsOnDrag = TRUE;
742
743 RegisterWindow();
744
745 m_waiting = TRUE;
746
747 GtkTargetList *target_list = gtk_target_list_new( (GtkTargetEntry*) NULL, 0 );
748
749 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
750 m_data->GetAllFormats( array );
751 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
752 {
753 GdkAtom atom = array[i];
754 wxLogDebug( wxT("Supported atom %s"), gdk_atom_name( atom ) );
755 gtk_target_list_add( target_list, atom, 0, 0 );
756 }
757 delete[] array;
758
759 GdkEventMotion event;
760 event.window = m_widget->window;
761 int x = 0;
762 int y = 0;
763 GdkModifierType state;
764 gdk_window_get_pointer( event.window, &x, &y, &state );
765 event.x = x;
766 event.y = y;
767 event.state = state;
768 event.time = GDK_CURRENT_TIME;
769
770 /* GTK wants to know which button was pressed which caused the dragging */
771 int button_number = 0;
772 if (event.state & GDK_BUTTON1_MASK) button_number = 1;
773 else if (event.state & GDK_BUTTON2_MASK) button_number = 2;
774 else if (event.state & GDK_BUTTON3_MASK) button_number = 3;
775
776 #if wxUSE_THREADS
777 /* disable GUI threads */
778 wxapp_uninstall_thread_wakeup();
779 #endif
780
781 /* don't start dragging if no button is down */
782 if (button_number)
783 {
784 GdkDragContext *context = gtk_drag_begin( m_widget,
785 target_list,
786 GDK_ACTION_COPY,
787 button_number, /* number of mouse button which started drag */
788 (GdkEvent*) &event );
789
790 wxMask *mask = m_goIcon.GetMask();
791 GdkBitmap *bm = (GdkBitmap *) NULL;
792 if (mask) bm = mask->GetBitmap();
793 GdkPixmap *pm = m_goIcon.GetPixmap();
794
795 gtk_drag_set_icon_pixmap( context,
796 gtk_widget_get_colormap( m_widget ),
797 pm,
798 bm,
799 0,
800 0 );
801
802 while (m_waiting) gtk_main_iteration();;
803 }
804
805 #if wxUSE_THREADS
806 /* re-enable GUI threads */
807 wxapp_install_thread_wakeup();
808 #endif
809
810 g_blockEventsOnDrag = FALSE;
811
812 UnregisterWindow();
813
814 return m_retValue;
815 }
816
817 void wxDropSource::RegisterWindow()
818 {
819 if (!m_widget) return;
820
821 gtk_signal_connect( GTK_OBJECT(m_widget), "drag_data_get",
822 GTK_SIGNAL_FUNC (source_drag_data_get), (gpointer) this);
823 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_data_delete",
824 GTK_SIGNAL_FUNC (source_drag_data_delete), (gpointer) this );
825 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_begin",
826 GTK_SIGNAL_FUNC (source_drag_begin), (gpointer) this );
827 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_end",
828 GTK_SIGNAL_FUNC (source_drag_end), (gpointer) this );
829
830 }
831
832 void wxDropSource::UnregisterWindow()
833 {
834 if (!m_widget) return;
835
836 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
837 GTK_SIGNAL_FUNC(source_drag_data_get), (gpointer) this );
838 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
839 GTK_SIGNAL_FUNC(source_drag_data_delete), (gpointer) this );
840 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
841 GTK_SIGNAL_FUNC(source_drag_begin), (gpointer) this );
842 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
843 GTK_SIGNAL_FUNC(source_drag_end), (gpointer) this );
844 }
845
846 #endif
847
848 // wxUSE_DRAG_AND_DROP