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