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